Setup

conda env on cluster

# Create env on cluster with mamba
mamba create -y \
  -n fst_env_rhel \
  -c bioconda gatk4
conda activate fst_env_rhel
mamba install bcftools plink2 r-base r-essentials r-tidyverse r-units libgdal r-sf
# Export
conda env export \
  --no-builds \
  -f envs/fst_env_rhel.yml
# Activate
conda activate fst_env_rhel

renv

# Export env (to renv.lock file)
renv::init()
# To install packages on new system, or 'activate' the env: 
renv::restore()

Source libraries, functions and plotting parameters

library(here)

source(here::here("code", "scripts", "source.R"))

Download 1KG data

Download from FTP

wget \
  -r -p -k \
  --no-parent \
  -cut-dirs=5 \
  ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/

Put filenames into list

find vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chr*.vcf.gz \
  > human_traits_fst/data/20200205_vcfs.list

Merge VCFs

java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=human_traits_fst/data/20200205_vcfs.list \
  O=vcfs/1kg_all.vcf.gz
# Exception in thread "main" java.lang.IllegalArgumentException: The contig entries in input file /hps/research1/birney/users/ian/rac_hyp/vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz are not compatible with the others.

# So remove that one from list above
sed -i '/MT/d' human_traits_fst/data/20200205_vcfs.list

# run MergeVCFs again
java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=human_traits_fst/data/20200205_vcfs.list \
  O=vcfs/1kg_all.vcf.gz
  
# Exception in thread "main" java.lang.IllegalArgumentException: The contig entries in input file /hps/research1/birney/users/ian/rac_hyp/vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chrY.phase3_integrated_v2a.20130502.genotypes.vcf.gz are not compatible with the others.
sed -i '/chrY/d' human_traits_fst/data/20200205_vcfs.list

# run MergeVCFs again
java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=human_traits_fst/data/20200205_vcfs.list \
  O=vcfs/1kg_all.vcf.gz
# SUCCESS

Obtain GWAS data from the GWAS Catalog https://www.ebi.ac.uk/gwas

Pull data for each trait

NOTE: Uncheck Include child trait data before downloading.

All documents downloaded via ‘Download Catalog data’ link, then collated and saved here: data/20210122_gwas_catalog.xlsx

Height

BMI

Educational attainment

Intelligence

IBD

Pigmentation

Read into list

file_in = here::here("data", "20210122_gwas_catalog.xlsx")
# Create vector of traits
traits = c("hei", "bmi", "edu", "int", "ibd", "pig")
names(traits) = traits
# Assign sheets to traits
sheets <- seq(1:11)
names(sheets) <- c("hei", "bmi", "edu", "int", "ibd", rep("pig", 6))

# get sheets
sheet_names <- readxl::excel_sheets(file_in)

# Create function to read in data
read_catalog_data <- function(path, target_sheet){
  # Read in data
  out = readxl::read_xlsx(path, sheet = target_sheet) %>% 
    dplyr::select(CHR = CHR_ID, 
                  POS = CHR_POS, 
                  SNP_AL = `STRONGEST SNP-RISK ALLELE`, 
                  P = `P-VALUE`, 
                  OR_OR_BETA = `OR or BETA`, 
                  MAPPED_TRAIT,
                  STUDY = `STUDY ACCESSION`,
                  SAMPLE = `INITIAL SAMPLE SIZE`) %>% 
    # Split SNP and risk allele into separate columns
    dplyr::mutate(TOP_SNP = stringr::str_split(SNP_AL, "-", simplify = T)[, 1],
                  RISK_ALLELE = stringr::str_split(SNP_AL, "-", simplify = T)[, 2]) %>% 
    # Reorder and select
    dplyr::select(CHR, POS, TOP_SNP, RISK_ALLELE, P, OR_OR_BETA, MAPPED_TRAIT, STUDY, SAMPLE)
  # Change variables to specific types
  out$CHR <- as.integer(out$CHR)
  out$POS <- as.numeric(out$POS)
  out$P <- as.numeric(out$P)
  # return DF
  return(out)
}

# Read in data
counter <- 0
data_list = lapply(traits, function(trait){
  # set counter 
  counter <<- counter + 1
  # set target file
  target_file = file_in
  # get target sheet
  target_sheet = sheets[names(sheets) == trait]
  length(target_sheet)
  # read in pigmentation data from multiple sheets and bind into single DF
  if (length(target_sheet) > 1){
    # loop over each sheet
    df <- lapply(target_sheet, function(sheet){
      out <- read_catalog_data(target_file,
                               target_sheet = sheet)
    })
    # set name of each DF to name of sheet (replacing spaces with underscores)
    names(df) = sheet_names[target_sheet] %>% 
      stringr::str_replace_all(" ", "_")
    # bind DFs into single DF
    df <- dplyr::bind_rows(df, .id = "PIG_PHENO")
  } 
  else {
    # read in other data
    df <- read_catalog_data(target_file,
                            target_sheet = target_sheet)
  }
  # Set PHENO column
  df$PHENO <- factor(trait, levels = trait_levels)
  # Recode PHENO
  df$PHENO = dplyr::recode(df$PHENO, !!!recode_vec)
  # Create list
  out = list()
  # Return DF as "raw"
  out[["raw"]] = df
  
  return(out)
})
NAs introduced by coercionExpecting numeric in AB1130 / R1130C28: got '1E-423'Expecting numeric in L2384 / R2384C12: got '2 x 9'Expecting numeric in M2384 / R2384C13: got '190120954 x 70383416'Expecting numeric in L3046 / R3046C12: got 'X'Expecting numeric in L3070 / R3070C12: got 'X'Expecting numeric in L4419 / R4419C12: got 'X'Expecting numeric in L4420 / R4420C12: got 'X'Expecting numeric in L4421 / R4421C12: got 'X'Expecting numeric in L4422 / R4422C12: got 'X'Expecting numeric in L4586 / R4586C12: got 'X'Expecting numeric in L6641 / R6641C12: got 'X'NAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercion
# How many SNPs in raw data
lapply(data_list, function(x) nrow(x[["raw"]]))
$hei
[1] 4912

$bmi
[1] 7573

$edu
[1] 3989

$int
[1] 2967

$ibd
[1] 536

$pig
[1] 1579
# Clean data
data_list = lapply(data_list, function(pheno){
  df_clean = pheno[["raw"]]
  # Remove rows with p-value of 0 (only 32 of them, associated with suntan)
  df_clean = df_clean[df_clean$P != 0, ]
  # Remove rows with NA in CHR
  df_clean = df_clean[!is.na(df_clean$CHR), ]
  # Remove duplicates
  ## Find SNPs that are duplicated
  dupes = unique(df_clean$TOP_SNP[duplicated(df_clean$TOP_SNP) | duplicated(df_clean$TOP_SNP, fromLast = T)])
  ## Select only 1 SNP from each set of duplicated SNPs
  dupe_filt = lapply(dupes, function(dupe){
    # Take the one with the lowest P-value
    min_p = min(df_clean$P[df_clean$TOP_SNP == dupe])
    out = df_clean[df_clean$TOP_SNP == dupe & df_clean$P == min_p, ]
    # If there are still duplicates due to having equal P, take the one with the largest effect size
    if (nrow(out) > 1 ){
      out = out[which.max(out$OR_OR_BETA), ]
    }
    return(out)
  })
  # Bind list into DF
  dupe_filt = dplyr::bind_rows(dupe_filt)
  # Extract non-duplicated rows
  non_dupe = df_clean[!duplicated(df_clean$TOP_SNP) & !duplicated(df_clean$TOP_SNP, fromLast = T), ]
  # Bind non-duplicated rows with filtered duplicates
  df_clean = rbind(non_dupe, dupe_filt) 
  # Add to list
  pheno[["clean"]] = df_clean
  
  return(pheno)
})

# New SNP count
lapply(data_list, function(x) nrow(x[["clean"]]))
$hei
[1] 4359

$bmi
[1] 4184

$edu
[1] 3021

$int
[1] 2544

$ibd
[1] 389

$pig
[1] 1048
lapply(data_list, function(pheno){
  knitr::kable(head(pheno[["clean"]]))
})

$hei

CHR POS TOP_SNP RISK_ALLELE P OR_OR_BETA MAPPED_TRAIT STUDY SAMPLE PHENO
10 31008555 rs2797910 G 6e-06 0.01036 body height GCST010059 up to 6,835 Korean ancestry individuals Height
3 68732085 rs1156293 G 1e-06 0.02157 body height GCST010059 up to 6,835 Korean ancestry individuals Height
10 98257696 rs1983864 G 6e-06 NA body height GCST005908 253,288 European ancestry individuals Height
11 117103213 rs12269901 C 2e-06 NA body height GCST005908 253,288 European ancestry individuals Height
11 18624296 rs11024739 A 9e-06 NA body height GCST005908 253,288 European ancestry individuals Height
11 8231306 rs110419 G 3e-07 NA body height GCST005908 253,288 European ancestry individuals Height
$bmi
CHR POS TOP_SNP RISK_ALLELE P OR_OR_BETA MAPPED_TRAIT STUDY SAMPLE PHENO
5 79549808 rs6899277 G 2e-06 0.70 body mass index GCST003647 1,235 Hispanic individuals, 706 Asian ancestry individuals, 1,549 African American individuals, 2,395 European ancestry individuals BMI
5 116877085 rs6866721 C 1e-07 0.76 body mass index GCST003647 1,235 Hispanic individuals, 706 Asian ancestry individuals, 1,549 African American individuals, 2,395 European ancestry individuals BMI
6 84154244 rs2497155 T 4e-06 1.05 body mass index GCST003647 1,235 Hispanic individuals, 706 Asian ancestry individuals, 1,549 African American individuals, 2,395 European ancestry individuals BMI
11 17737040 rs7926805 C 1e-06 0.80 body mass index GCST003647 1,235 Hispanic individuals, 706 Asian ancestry individuals, 1,549 African American individuals, 2,395 European ancestry individuals BMI
7 137720035 rs10234581 C 2e-06 NA body mass index GCST003248 40 Korean ancestry low BMI individuals, 37 Korean ancestry high BMI individuals BMI
11 56539377 rs2187511 G 1e-06 NA body mass index GCST003248 40 Korean ancestry low BMI individuals, 37 Korean ancestry high BMI individuals BMI
$edu
CHR POS TOP_SNP RISK_ALLELE P OR_OR_BETA MAPPED_TRAIT STUDY SAMPLE PHENO
18 79815871 rs34945223 ? 0 NA self reported educational attainment GCST007037 approximately 455,000 European ancestry individuals Educational attainment
19 4474728 rs76608582 ? 0 NA self reported educational attainment GCST007037 approximately 455,000 European ancestry individuals Educational attainment
19 18224905 rs4808766 ? 0 NA self reported educational attainment GCST007037 approximately 455,000 European ancestry individuals Educational attainment
19 30256794 rs55800473 ? 0 NA self reported educational attainment GCST007037 approximately 455,000 European ancestry individuals Educational attainment
19 35761593 rs807478 ? 0 NA self reported educational attainment GCST007037 approximately 455,000 European ancestry individuals Educational attainment
20 38884055 rs208795 ? 0 NA self reported educational attainment GCST007037 approximately 455,000 European ancestry individuals Educational attainment
$int
CHR POS TOP_SNP RISK_ALLELE P OR_OR_BETA MAPPED_TRAIT STUDY SAMPLE PHENO
2 156027428 rs72904190 A 0 0.0284595 intelligence GCST005316 120,934 British ancestry individuals, 127,548 individuals Intelligence
2 156055088 rs72906064 T 0 0.0284257 intelligence GCST005316 120,934 British ancestry individuals, 127,548 individuals Intelligence
2 156667523 rs13023735 A 0 0.0180964 intelligence GCST005316 120,934 British ancestry individuals, 127,548 individuals Intelligence
2 160506319 rs62175972 T 0 0.0467504 intelligence GCST005316 120,934 British ancestry individuals, 127,548 individuals Intelligence
2 161045556 rs10930011 A 0 0.0168607 intelligence GCST005316 120,934 British ancestry individuals, 127,548 individuals Intelligence
2 160955841 rs114952970 T 0 0.0512996 intelligence GCST005316 120,934 British ancestry individuals, 127,548 individuals Intelligence
$ibd
CHR POS TOP_SNP RISK_ALLELE P OR_OR_BETA MAPPED_TRAIT STUDY SAMPLE PHENO
1 154962487 rs3766920 A 0e+00 1.35 inflammatory bowel disease GCST003602 1,505 Korean ancestry cases, 4,041 Korean ancestry controls IBD
16 80751551 rs16953946 C 0e+00 1.23 inflammatory bowel disease GCST003602 1,505 Korean ancestry cases, 4,041 Korean ancestry controls IBD
22 36912743 rs4821558 C 8e-07 1.16 inflammatory bowel disease GCST003602 1,505 Korean ancestry cases, 4,041 Korean ancestry controls IBD
9 76302462 rs11788518 T 7e-06 1.39 inflammatory bowel disease GCST003602 1,505 Korean ancestry cases, 4,041 Korean ancestry controls IBD
9 21966222 rs3731257 G 0e+00 1.17 inflammatory bowel disease GCST003602 1,505 Korean ancestry cases, 4,041 Korean ancestry controls IBD
6 116919430 rs1321366 A 5e-06 1.15 inflammatory bowel disease GCST003602 1,505 Korean ancestry cases, 4,041 Korean ancestry controls IBD
$pig
PIG_PHENO CHR POS TOP_SNP RISK_ALLELE P OR_OR_BETA MAPPED_TRAIT STUDY SAMPLE PHENO
skin_pigmentation 3 187681148 rs79592764 T 4e-07 5.23 skin pigmentation GCST004219 285 Puerto Rican individuals Pigmentation
skin_pigmentation 10 13564490 rs6602666 G 0e+00 4.03 skin pigmentation GCST004219 285 Puerto Rican individuals Pigmentation
skin_pigmentation 15 50016753 rs8033655 G 2e-07 2.29 skin pigmentation GCST004219 285 Puerto Rican individuals Pigmentation
skin_pigmentation 20 34116821 rs6142102 G 2e-07 2.21 skin pigmentation GCST004219 285 Puerto Rican individuals Pigmentation
skin_pigmentation 1 188031112 rs77443641 A 3e-06 10.94 skin pigmentation GCST004219 285 Puerto Rican individuals Pigmentation
skin_pigmentation 1 196973530 rs115105970 T 2e-06 6.84 skin pigmentation GCST004219 285 Puerto Rican individuals Pigmentation
NA

Get unique mapped traits

lapply(data_list, function(pheno){
  unique(pheno$clean$MAPPED_TRAIT)
})
$hei
[1] "body height"                                                     
[2] "diastolic blood pressure, body height"                           
[3] "body weights and measures, body height"                          
[4] "anthropometric measurement, body height"                         
[5] "pericardial adipose tissue measurement, body weight, body height"

$bmi
 [1] "body mass index"                                                                                                                                                                                                                                                                                                                                        
 [2] "systolic blood pressure, body mass index"                                                                                                                                                                                                                                                                                                               
 [3] "body mass index, obesity"                                                                                                                                                                                                                                                                                                                               
 [4] "response to drug, body mass index"                                                                                                                                                                                                                                                                                                                      
 [5] "body mass index, body weights and measures"                                                                                                                                                                                                                                                                                                             
 [6] "body mass index, fasting blood insulin measurement"                                                                                                                                                                                                                                                                                                     
 [7] "alcohol consumption measurement, body mass index"                                                                                                                                                                                                                                                                                                       
 [8] "physical activity measurement, body mass index"                                                                                                                                                                                                                                                                                                         
 [9] "smoking behaviour measurement, body mass index"                                                                                                                                                                                                                                                                                                         
[10] "waist-hip ratio, body mass index"                                                                                                                                                                                                                                                                                                                       
[11] "adult onset asthma, body mass index"                                                                                                                                                                                                                                                                                                                    
[12] "multiple sclerosis, body mass index"                                                                                                                                                                                                                                                                                                                    
[13] "anthropometric measurement, body mass index"                                                                                                                                                                                                                                                                                                            
[14] "visceral adipose tissue measurement, body mass index"                                                                                                                                                                                                                                                                                                   
[15] "body mass index, premature birth, parental genotype effect measurement"                                                                                                                                                                                                                                                                                 
[16] "bipolar disorder, body mass index"                                                                                                                                                                                                                                                                                                                      
[17] "total cholesterol measurement, diastolic blood pressure, triglyceride measurement, systolic blood pressure, hematocrit, ventricular rate measurement, glucose measurement, body mass index, high density lipoprotein cholesterol measurement"                                                                                                           
[18] "Barrett's esophagus, body mass index"                                                                                                                                                                                                                                                                                                                   
[19] "body mass index, asthma"                                                                                                                                                                                                                                                                                                                                
[20] "insulin sensitivity measurement, body mass index"                                                                                                                                                                                                                                                                                                       
[21] "body mass index, visceral:subcutaneous adipose tissue ratio"                                                                                                                                                                                                                                                                                            
[22] "total cholesterol measurement, hematocrit, stroke, ventricular rate measurement, body mass index, atrial fibrillation, high density lipoprotein cholesterol measurement, coronary artery disease, cancer, diastolic blood pressure, triglyceride measurement, systolic blood pressure, heart failure, diabetes mellitus, glucose measurement, mortality"
[23] "fibrinogen measurement, body mass index"                                                                                                                                                                                                                                                                                                                
[24] "urate measurement, body mass index"                                                                                                                                                                                                                                                                                                                     
[25] "body mass index, fasting blood glucose measurement"                                                                                                                                                                                                                                                                                                     
[26] "body mass index, age at assessment"                                                                                                                                                                                                                                                                                                                     
[27] "smoking behavior, body mass index"                                                                                                                                                                                                                                                                                                                      
[28] "diet measurement, body mass index"                                                                                                                                                                                                                                                                                                                      
[29] "sex interaction measurement, body mass index, age at assessment"                                                                                                                                                                                                                                                                                        
[30] "physical activity, body mass index"                                                                                                                                                                                                                                                                                                                     

$edu
[1] "self reported educational attainment"                              
[2] "intelligence, self reported educational attainment"                
[3] "autism spectrum disorder, self reported educational attainment"    
[4] "schizophrenia, intelligence, self reported educational attainment" 
[5] "self reported educational attainment, refractive error measurement"
[6] "systolic blood pressure, self reported educational attainment"     
[7] "diastolic blood pressure, self reported educational attainment"    
[8] "mean arterial pressure, self reported educational attainment"      
[9] "pulse pressure measurement, self reported educational attainment"  

$int
[1] "intelligence"                                                     
[2] "cesarean section, intelligence"                                   
[3] "intelligence, specific language impairment, dyslexia"             
[4] "intelligence, self reported educational attainment"               
[5] "schizophrenia, intelligence"                                      
[6] "schizophrenia, intelligence, self reported educational attainment"

$ibd
 [1] "inflammatory bowel disease"                                                                           
 [2] "inflammatory bowel disease, spine bone mineral density"                                               
 [3] "leukopenia, response to thiopurine, inflammatory bowel disease"                                       
 [4] "inflammatory bowel disease, femoral neck bone mineral density"                                        
 [5] "pyoderma gangrenosum, inflammatory bowel disease"                                                     
 [6] "Erythema nodosum, inflammatory bowel disease"                                                         
 [7] "myelosuppression, response to thiopurine, inflammatory bowel disease"                                 
 [8] "Alopecia, response to thiopurine, inflammatory bowel disease"                                         
 [9] "pancreatitis, response to thiopurine, inflammatory bowel disease"                                     
[10] "response to thiopurine, thiopurine immunosuppressant-induced pancreatitis, inflammatory bowel disease"
[11] "digestive system disease, response to thiopurine, inflammatory bowel disease"                         

$pig
[1] "skin pigmentation"                                   
[2] "freckles, skin pigmentation"                         
[3] "skin pigmentation measurement"                       
[4] "eye color"                                           
[5] "eye colour measurement"                              
[6] "hair color"                                          
[7] "cutaneous melanoma, hair color"                      
[8] "hair colour measurement"                             
[9] "hair colour measurement, hair morphology measurement"

Are any of the OR_OR_BETAs negative?

any(unlist(lapply(data_list, function(pheno) {
  any(pheno$clean$OR_OR_BETA < 0,na.rm = T)
})))
[1] FALSE

This must means that the RISK_ALLELE always affects the trait positively.

But for what proportion of SNPs is the RISK_ALLELE not stated?

lapply(data_list, function(pheno) {
  length(which(pheno$clean$RISK_ALLELE == "?")) / nrow(pheno$clean)
})
$hei
[1] 0.7065841

$bmi
[1] 0.5023901

$edu
[1] 0.1741145

$int
[1] 0.4382862

$ibd
[1] 0.3213368

$pig
[1] 0.5314885

NOTE: In cases where RISK_ALLELE isn’t provided, treat the ALT allele as RISK_ALLELE.

Generate Manhattan plots

Plot

counter = 0
lapply(data_list, function(pheno_df){
  # set counter
  counter <<- counter + 1
  df = pheno_df[["clean"]]
  trait = unique(df$PHENO)
  # Get title
  title <- paste(trait, "\n", "SNP count:", nrow(df))
  # Plot
  get_man(df, trait = trait, title = title, chr = "CHR", bp = "POS", snp = "TOP_SNP", p = "P")
})

Save

output_dir = here::here("plots", "20210122_manhattan_all_snps")

counter = 0
lapply(data_list, function(pheno_df){

  # set counter
  counter <<- counter + 1
  df = pheno_df[["clean"]]
  trait = unique(df$PHENO)
  # Get title
  title <- paste(trait, "\n", "SNP count:", nrow(df))
  # Set file name to save
  file = file.path(output_dir,
                   paste("manhattan_",
                         names(data_list)[counter],
                         ".svg",
                         sep = ""))
  # Set up graphics device
  svg(file,
      width = 10,
      height = 6)  
  
  # Plot
  get_man(df, trait = trait, chr = "CHR", bp = "POS", snp = "TOP_SNP", p = "P")
  
  dev.off()
})

Create list of target SNPs to extract from 1KG

dest_dir = here::here("data", "20210122_snp_hit_lists")

# Make directory
dir.create(dest_dir)
  
# Just SNPs for extracting from 1KG
counter <- 0
lapply(data_list, function(pheno){
  df = pheno[["clean"]]
  # Set counter
  counter <<- counter + 1
  # Set file basename
  trait = names(data_list)[counter]
  filename = paste(trait, ".list", sep = "")
  # Write SNPs to file
  readr::write_lines(df$TOP_SNP, file.path(dest_dir, filename))
})

# SNPs and P-values with header for clumping with Plink
counter <- 0
lapply(data_list, function(pheno){
  df = pheno[["clean"]]
  # Set counter
  counter <<- counter + 1
  # Set file basename
  trait = names(data_list)[counter]
  filename = paste(trait, "_with_P.txt", sep = "")
  # Write SNPs to file
  df %>% 
    dplyr::select(SNP = TOP_SNP, P) %>% 
    readr::write_tsv(file.path(dest_dir, filename))
})

Filter 1KG VCF for target SNPs

traits=$(echo hei bmi edu int ibd pig)
ref=../refs/hs37d5.fa.gz
in_vcf=../vcfs/1kg_all.vcf.gz
snps_dir=data/20210122_snp_hit_lists
out_dir=data/20210125_snp_hits_filtered

mkdir -p $out_dir

for trait in $(echo $traits ); do
  bsub \
    -M 10000 \
    -o ../log/20210122_extract_snps_$trait.out \
    -e ../log/20210122_extract_snps_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_vcf \
      --keep-ids $snps_dir/$trait.list \
      -O $out_dir/$trait.vcf.gz 
    """ ;
done

Get allele frequencies of SNP hits with Plink2

Import 1KG metadata (for sample-population key)

Downloded via this page: http://www.internationalgenome.org/data.

Download link: http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/technical/working/20130606_sample_info/20130606_sample_info.xlsx.

Saved here: data/20130606_sample_info.xlsx

samples_file = here::here("data", "20130606_sample_info.xlsx")

meta = readxl::read_xlsx(samples_file,
                         sheet = "Sample Info") %>%
  dplyr::select(Sample, Population, Gender)

knitr::kable(head(meta))
Sample Population Gender
HG00096 GBR male
HG00097 GBR female
HG00098 GBR male
HG00099 GBR female
HG00100 GBR female
HG00101 GBR male

Write population file for Plink2

sample_popn_key_file = here::here("data", "plink2_sample_popn_key.txt")

write.table(meta[, 1:2],
            sample_popn_key_file,
            quote = F,
            sep = "\t",
            row.names = F,
            col.names = F)

Run Plink2 for SNP hits

(Take only biallelic SNPs.)

conda activate fst_env_rhel

# Set variables

traits=$(echo hei bmi edu int ibd pig)
in_vcf_dir=data/20210125_snp_hits_filtered
popn_file=data/plink2_sample_popn_key.txt
out_dir=data/20210122_snp_hits_alfreqs

# Set up directories
mkdir -p $out_dir

for trait in $(echo $traits); do
  mkdir -p $out_dir/$trait; 
done   

# Run Plink2

## Get global AF
for trait in $(echo $traits); do
  plink2 \
    --vcf $in_vcf_dir/$trait.vcf.gz \
    --freq \
    --max-alleles 2 \
    --snps-only \
    --out $out_dir/$trait/$trait.all
done

## Get AF per population
for trait in $(echo $traits); do
  plink2 \
    --vcf $in_vcf_dir/$trait.vcf.gz \
    --freq \
    --max-alleles 2 \
    --snps-only \
    --pheno iid-only $popn_file \
    --loop-cats PHENO1 \
    --out $out_dir/$trait/$trait ;
done

Add allele frequency files

target_dir = here::here("data", "20210122_snp_hits_alfreqs")

# Global
counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter 
  counter <<- counter + 1
  # get trait name
  trait = names(data_list)[counter]
  # get file path
  target_path = file.path(target_dir, trait, paste(trait, ".all.afreq", sep = ""))
  # read in data
  clean_af = read_afreq(target_path)
  # add POPN column
  clean_af$POPN = "all"
  # add to list
  pheno[["clean_af"]] = clean_af
  
  return(pheno)
})

# Per population
counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # get trait name
  trait = names(data_list)[counter]
  # get file path
  target_files = list.files(file.path(target_dir, trait),
                            pattern = ".*[^all]\\.afreq",
                            full.names = T)
  # get popn names
  names(target_files) = basename(target_files) %>% 
    str_split("\\.", simplify = T) %>% 
    subset(select = 2)
  # read files and bind into single DF
  popn_afreqs = lapply(target_files, function(popn){
    df = read_afreq(popn)
  }) %>% 
    dplyr::bind_rows(.id = "POPN")# %>% 
#    dplyr::select(-OBS_CT) %>% 
#    tidyr::pivot_wider(id_cols = SNP, names_from = POPN, values_from = ALT_FREQS)
  # combine with `clean_af`
  pheno[["clean_af"]] = dplyr::bind_rows(pheno[["clean_af"]],
                                         popn_afreqs)
  
  return(pheno)
})

Set up negative controls

Pull out random SNPs with the same global allele frequencies as the GWAS SNP-hits

Bin SNP hits by allele frequency

Bind to clean DF to get AFs of risk allele

NOTE: If RISK_ALLELE is unknown, set the allele frequency to ALT_FREQS

data_list = lapply(data_list, function(pheno){
  # join DFs
  df = dplyr::left_join(pheno[["clean"]],
                        dplyr::select(pheno[["clean_af"]],
                                      -CHR),
                        by = c("TOP_SNP" = "SNP"))
  # get AF of risk allele
  df$RISK_AF = dplyr::if_else(df$RISK_ALLELE == "?",
                              df$ALT_FREQS,
                              dplyr::if_else(df$RISK_ALLELE == df$ALT,
                                             df$ALT_FREQS,
                                             1 - df$ALT_FREQS))
  # add `HIT_CONTROL` column
  df$HIT_CONTROL = "hit"
  # add to list
  pheno[["consol"]] = df
  
  return(pheno)
})

Bin by risk allele frequency

# 1% intervals
breakpoints = seq(0, 1, 0.1)

data_list = lapply(data_list, function(pheno){
  # choose DF
  df = pheno[["consol"]]
  # add bins
  df$BIN_10 = cut(df$RISK_AF, breaks = breakpoints, labels = F)
  # save back into list
  pheno[["consol"]] = df
  
  return(pheno)
})

Extract key columns and write to file

out_dir = here::here("data", "20210201_snp_risk_hits_binned")

dir.create(out_dir)

# Save list
counter <- 0
risk_afs = lapply(data_list, function(pheno){
  # set counter 
  counter <<- counter + 1
  # get target DF
  df = pheno[["consol"]]
  # filter
  df = df %>% 
    dplyr::filter(POPN == "all") %>% # take only global AFs 
    dplyr::select(TOP_SNP, BIN_10) %>% 
    tidyr::drop_na() # drop NAs
  # set output path
  trait = names(data_list)[counter]
  path_out = file.path(out_dir, paste(trait, ".txt", sep = ""))
  # write to file
  readr::write_tsv(df, path_out)
})

Bin 1KG SNPs

Get allele frequencies from 1KG

With Plink2, per chromosome for speed.

# set output directory
in_file=../vcfs/1kg_all.vcf.gz
out_dir=../big_data/20210125_alfreqs_all

mkdir -p $out_dir

# Per chromosome
for chr in $(seq 1 22) ; do
  # create allele-freq tables
  bsub \
    -M 10000 \
    -o ../log/20210125_plink_alfreq_$chr.out \
    -e ../log/20210125_plink_alfreq_$chr.err \
    """
    conda activate fst_env_rhel ;
    plink2 \
      --vcf $in_file \
      --freq \
      --chr $chr \
      --max-alleles 2 \
      --snps-only \
      --out $out_dir/$chr ";
done 

Bin them and save to single file

# On cluster
# `conda activate fst_env_rhel`

library(here)
source(here::here("code", "scripts", "source.R"))

# Set variables
in_dir = "../big_data/20210125_alfreqs_all"
out_dir = "../big_data/20210201_alfreqs_all_binned_10"
breakpoints = seq(0, 1, 0.1) # 1% bins

# Create output directory
dir.create(out_dir)

# Get list of input files
in_files = list.files(in_dir, pattern = ".afreq", full.names = T)

# Read in files, add bins, and write to output
freq_list = lapply(in_files, function(chr_file){
  # read in file
  df = read_afreq(chr_file)
  # add bins
  df$BIN_10 = cut(df$ALT_FREQS, breaks = breakpoints, labels = F)
  # write file
  readr::write_tsv(df, file = file.path(out_dir, basename(chr_file)))
})

# Combine into single DF
freq_df = dplyr::bind_rows(freq_list)

# Write to file
readr::write_tsv(freq_df, file = file.path(out_dir, "all.afreq"))

Pull out random SNPs with same AF as trait risk alleles

# On cluster

library(here)
source(here::here("code", "scripts", "source.R"))

# Variables

input_risk_snp_dir = here::here("data", "20210201_snp_risk_hits_binned")
all_1kg_bins = "../big_data/20210201_alfreqs_all_binned/all.afreq"
initial_seed = 123
output_dir = here::here("data", "20210201_random_snps")
output_dir_snpids = here::here("data", "20210201_random_snps_snp_ids")

dir.create(output_dir)
dir.create(output_dir_snpids)

## Read in target SNP DF and split into list by bin
phenos = gsub(".txt", "", basename(list.files(input_risk_snp_dir)))
names(phenos) = phenos

risk_list = lapply(phenos, function(pheno){
  # set file path
  file_path = file.path(input_risk_snp_dir, paste(pheno, ".txt", sep = ""))
  # read file
  out = readr::read_tsv(file_path,
                        col_names = T) %>% 
    split(., f = .$BIN_100) # split by bin
})
  
## Read in 1KG data
freq_df = readr::read_tsv(all_1kg_bins)

# For each bin in `risk_list`, pull out the same number of random number 1KG SNPs with the same bin

## Set seed
set.seed(initial_seed)

## Get seeds for each bin
seeds = sample(1:1000, length(risk_list))

## Run over list
counter <- 0
lapply(risk_list, function(pheno){
  # set counter 
  counter <<- counter + 1  
  # set seed for pheno
  set.seed(seeds)[counter]
  # get seeds for bin
  bin_seeds = sample(1:1000, length(pheno))
  # get random SNPs from each bin
  bin_counter <- 0
  out = lapply(pheno, function(bin_df){
    # set `bin_counter`
    bin_counter <<- bin_counter + 1
    # get target bin
    target_bin = as.integer(names(pheno)[bin_counter])
    # get number of matches required
    hits_n = nrow(bin_df)
    # set seed
    set.seed(bin_seeds[bin_counter])    
    # filter 1kg DF for SNPs with same bin and get random hits
    random_hits = freq_df %>% 
      #dplyr::select(SNP, ALT_FREQS, OBS_CT, BIN_100) %>% 
      dplyr::filter(BIN_100 == target_bin) %>% 
      dplyr::slice_sample(n = hits_n) %>% 
      dplyr::rename(RANDOM_SNP = SNP,
                    RANDOM_BIN_100 = BIN_100)
    # bind `random_hits` to target SNP df
    df_out = cbind(bin_df, random_hits)
    
    return(df_out)
  }) %>% 
    # bind into single data frame
    dplyr::bind_rows()
  
  # save to file
  ## set output path
  trait = names(risk_list)[counter]
  out_path = file.path(output_dir, paste(trait, ".txt", sep = ""))
  ## write file
  readr::write_tsv(out, out_path)
  
  # save just SNP IDs (for Plink to get per-population AFs)
  out_path = file.path(output_dir_snpids, paste(trait, ".list", sep = ""))
  ## write file
  readr::write_lines(out$RANDOM_SNP, out_path)  
})

Filter VCFs for random SNPs

traits=$(echo hei bmi edu int ibd pig)
ref=../refs/hs37d5.fa.gz
in_vcf=../vcfs/1kg_all.vcf.gz
snps_dir=data/20210126_random_snps_snp_ids
out_dir=data/20210127_snp_rndm_filtered

mkdir -p $out_dir

for trait in $(echo $traits ); do
  bsub \
    -M 10000 \
    -o ../log/20210127_extract_snps_$trait.out \
    -e ../log/20210127_extract_snps_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_vcf \
      --keep-ids $snps_dir/$trait.list \
      -O $out_dir/$trait.vcf.gz 
    """ ;
done

Get per-population allele frequencies of random SNPs

traits=$(echo hei bmi edu int ibd pig)
random_snps_dir=data/20210126_random_snps_snp_ids
vcf_in_dir=data/20210127_snp_rndm_filtered
popn_key=data/plink2_sample_popn_key.txt
out_dir=data/20210127_snp_rndm_alfreqs


for trait in $(echo $traits ); do

  mkdir -p $out_dir/$trait
  
  bsub \
    -M 10000 \
    -o ../log/20210127_rdm_popn_afreqs_$trait.out \
    -e ../log/20210127_rdm_popn_afreqs_$trait.err \
    """
    conda activate fst_env_rhel ;
    plink2 \
      --vcf $vcf_in_dir/$trait.vcf.gz \
      --extract $random_snps_dir/$trait.list \
      --freq \
      --pheno iid-only $popn_key \
      --loop-cats PHENO1 \
      --out $out_dir/$trait/$trait
    """ ;
done  

Bind into single DF

in_dir_rndm = here::here("data", "20210126_random_snps")
in_dir_afreq = here::here("data", "20210127_snp_rndm_alfreqs")

# Read in data

## Random SNPs
in_files_rndm = list.files(in_dir_rndm, full.names = T)
names(in_files_rndm) = gsub(".txt", "", basename(in_files_rndm))

random_snps = lapply(in_files_rndm, function(file){
  out = readr::read_tsv(file)
  # add `POPN` column
  out$POPN = "all"
  
  return(out)
}) %>% 
  dplyr::bind_rows(.id = "PHENO")

## Popn afreqs
popn_afreqs = lapply(trait_levels, function(pheno){
  target_files = list.files(file.path(in_dir_afreq, pheno), pattern = ".afreq", full.names = T)
  
  names(target_files) = basename(target_files) %>% 
    str_split("\\.", simplify = T) %>% 
    subset(select = 2)
  
  popn_afreqs = lapply(target_files, function(popn){
    df = read_afreq(popn)
  }) %>% 
    dplyr::bind_rows(.id = "POPN") # %>% 
  #  dplyr::select(-OBS_CT) %>% 
  #  tidyr::pivot_wider(id_cols = SNP, names_from = POPN, values_from = c(ALT_FREQS, OBS_CT))
  
  return(popn_afreqs)
}) %>% 
  dplyr::bind_rows(.id = "PHENO")

# Bind `popn_afreqs` to `random_snps`
random_afreqs = dplyr::full_join(dplyr::select(random_snps, -c(POPN, ALT_FREQS, OBS_CT)),
                                 popn_afreqs,
                                 by = c("PHENO", "RANDOM_SNP" = "SNP", "REF", "ALT", "CHR"))
# Bind `all` AFs
random_afreqs = rbind(random_afreqs, random_snps)

## Recode PHENO
#random_afreqs$PHENO = dplyr::recode(random_afreqs$PHENO, !!!rev_recode_vec)

## Add `HIT_CONTROL` column
random_afreqs$HIT_CONTROL = "control"

Add to data_list

counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # set target pheno
  target_pheno = names(data_list)[counter]
  # add random afreqs
  random_af = random_afreqs %>% 
    dplyr::filter(PHENO == target_pheno)
  # recode PHENO
  random_af$PHENO = dplyr::recode(random_af$PHENO, !!!recode_vec)
  # add to list
  pheno[["random_af"]] = random_af
  
  return(pheno)
})

Clump to get lead SNPs

Use Plink1.9 (Plink2.0 doesn’t have a clump function.)

From the Plink1.7 documentation (http://zzz.bwh.harvard.edu/plink/clump.shtml), which applies to Plink1.9:

The clumping procedure takes all SNPs that are significant at threshold p1 that have not already been clumped (denoting these as index SNPs) and forms clumps of all other SNPs that are within a certain kb distance from the index SNP (default 250kb) and that are in linkage disequilibrium with the index SNP, based on an r-squared threshold (default 0.50)… This is a greedy algorithm and so each SNP will only appear in a single clump, if at all.

…[t]he TOTAL field lists all SNPs that are clumped with the index SNP, irrespective of the p-value for those SNPs. This number is then split into those clumped SNPs that are not significant (p>0.05) and various other groups defined by significance thresholds. For SNPs that are significant at the p2 threshold, they are listed explicitly. The (1) after each SNP name refers to the results file they came from (in this case, there is only a single result file specified, so all values are 1).

Here, we’re taking all SNPs with P < 1e-08 as index SNPs, and it will explicitly list all SNPs within the clump that also meet that threshold.

# Activate environment
conda activate fst_env_rhel

# Set variables
traits=$(echo hei bmi edu int ibd pig)
in_vcf_dir=data/20210125_snp_hits_filtered
snp_p_dir=data/20210122_snp_hit_lists
out_dir=data/20210125_clumped

r2_params=$(echo 0.1 0.2 0.3)
kb_params=$(echo 500 750 1000 )

# Make directory
mkdir -p $out_dir

# Run with different parameters
for trait in $(echo $traits ); do

  mkdir -p $out_dir/$trait ;
  
  for r2 in $r2_params ; do
    for kb in $kb_params ; do
      bsub \
        -o ../log/20210125_clump_$trait\_$r2\_$kb.out \
        -e ../log/20210125_clump_$trait\_$r2\_$kb.err \
        """
        conda activate fst_env_rhel ;
        plink \
          --vcf $in_vcf_dir/$trait.vcf.gz \
          --clump $snp_p_dir/$trait\_with_P.txt \
          --clump-p1 0.00000001 \
          --clump-p2 0.00000001 \
          --clump-r2 $r2 \
          --clump-kb $kb \
          --out $out_dir/$trait/r2-$r2\_kb-$kb 
        """  ;
    done ;
  done;  
done

Filter VCFs for clumped SNPs

# On cluster

# Set variables
traits=$(echo hei bmi edu int ibd pig)
clump_param="r2-0.1_kb-1000"
in_dir_snp=data/20210125_clumped
in_dir_vcf=data/20210125_snp_hits_filtered
ref=../refs/hs37d5.fa.gz
out_dir_snp_list=data/20210128_clumped_snps
out_dir_vcfs=data/20210128_hits_vcfs

mkdir -p $out_dir_snp_list $out_dir_vcfs

# Extract SNP column and write to list
for trait in $(echo $traits ); do
  target_file=$in_dir_snp/$trait/$clump_param.clumped ;
  awk '{print $3}' $target_file | tail -n+2 \
    > $out_dir_snp_list/$trait.list ;
done

# Make new VCFs
for trait in $(echo $traits ); do
  bsub \
    -o ../log/20210128_filter_vcfs_clump_$trait.out \
    -e ../log/20210128_filter_vcfs_clump_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_dir_vcf/$trait.vcf.gz \
      --keep-ids $out_dir_snp_list/$trait.list \
      -O $out_dir_vcfs/$trait.vcf.gz     
    """ ;
done    

Add clumped SNP files to data_list

target_dir = here::here("data", "20210125_clumped")

counter <- 0
data_list = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # get trait name
  trait = names(data_list)[counter]
  # get file path
  target_files = list.files(file.path(target_dir, trait),
                            pattern = ".clumped",
                            full.names = T)
  names(target_files) = gsub(".clumped", "", basename(target_files))
  # read files as `clumped`
  counter_clump <- 0
  clumped = lapply(target_files, function(params){
    # set counter
    counter_clump <<- counter_clump + 1
    # split params string
    param_str = names(target_files)[counter_clump] %>% 
      stringr::str_split("_", simplify = T) %>%
      stringr::str_split("-", simplify = T)
    # get params
    r2 = as.numeric(param_str[1,2])
    kb = as.integer(param_str[2,2])
    # read files
    df = read.table(params, header = T)
    # add params to DF
    df$r2 = r2
    df$kb = kb
    
    return(df)
  })
  # add `clumped` list to `data_list`
  pheno[["clumped"]] = clumped
  # bind `clumped` into single DF and add to `data_list`
  pheno[["clumped_all"]] = dplyr::bind_rows(clumped)
  
  return(pheno)
})

Extract random SNPs filtered by those matching the clumped SNPs

out_dir = here::here("data", "20210128_random_snps")

dir.create(out_dir)

counter = 0
out = lapply(data_list, function(pheno){
  # set counter
  counter <<- counter + 1
  # get name of trait
  trait = names(data_list)[counter]
  # get clumped SNPs
  clumped_snps = pheno[["clumped"]][[clump_param]]$SNP
  # gather SNPs
  snps = pheno[["random_af"]] %>% 
    dplyr::filter(TOP_SNP %in% clumped_snps) %>% 
    dplyr::select(RANDOM_SNP) %>% 
    unique(.)
  # write to file
  out_path = file.path(out_dir, paste(trait, ".list", sep = ""))
  readr::write_lines(snps$RANDOM_SNP, out_path)
})
rm(out)

Make VCFs for random control SNPs

# On cluster

# Set variables
traits=$(echo hei bmi edu int ibd pig)
in_dir_snp_list=data/20210128_random_snps
in_vcf=../vcfs/1kg_all.vcf.gz
ref=../refs/hs37d5.fa.gz
out_dir_vcfs=data/20210128_rndm_vcfs

mkdir -p $out_dir_vcfs

# Make new VCFs
for trait in $(echo $traits ); do
  bsub \
    -M 20000 \
    -o ../log/20210128_filter_vcfs_rndm_$trait.out \
    -e ../log/20210128_filter_vcfs_rndm_$trait.err \
    """
    conda activate fst_env_rhel ;
    gatk SelectVariants \
      -R $ref \
      -V $in_vcf \
      --keep-ids $in_dir_snp_list/$trait.list \
      -O $out_dir_vcfs/$trait.vcf.gz     
    """ ;
done    

Consolidate key data into single DF for analysis

NOTE: clump_param is set in code/scripts/source.R

data_list = lapply(data_list, function(pheno){
  # Filter `consol` by the index SNPs in target `clump`
  target_clump = pheno[["clumped"]][[clump_param]]
  
  final = pheno[["consol"]] %>% 
    dplyr::rename(SNP = TOP_SNP) %>% 
    dplyr::filter(SNP %in% target_clump$SNP) 
  
  # Add allele frequencies of SNP hits (global and per-population)
  
  # Add controls
  controls = pheno[["random_af"]] %>%
    # filter for SNPs in target_clump
    dplyr::filter(TOP_SNP %in% target_clump$SNP) %>% 
    dplyr::select(-c(TOP_SNP, BIN_100, RANDOM_BIN_100), 
                  SNP = RANDOM_SNP) %>% 
    dplyr::mutate(RISK_AF = ALT_FREQS)
  
  final = dplyr::bind_rows(final, controls)  
  pheno[["final"]] = final
  
  return(pheno)
})

# Create final df
final_df = lapply(data_list, function(pheno){
  out = pheno[["final"]]
  
  return(out)
}) %>% 
  dplyr::bind_rows()

# Set factors
final_df$PHENO <- factor(final_df$PHENO, levels = trait_levels_verb)
final_df$HIT_CONTROL = factor(final_df$HIT_CONTROL, levels = hit_control_levels)

# Create DF for plotting
final_plt = final_df %>% 
  dplyr::select(SNP, PHENO, POPN, RISK_AF, HIT_CONTROL) %>% 
  tidyr::pivot_wider(names_from = POPN, values_from = RISK_AF) 

Analysis

Manhattan plots

Allele frequency distributions of risk alleles

final_df %>%
  dplyr::filter(HIT_CONTROL == "hit") %>% 
  ggplot(aes(RISK_AF, fill = PHENO)) +
    geom_histogram(bins = 100) +
    scale_fill_manual(values = pal_primary) +
    facet_wrap(vars(PHENO), nrow = 2) +
    guides(fill = F) +
    xlab("Risk allele frequency") +
    ylab("Count") +
    theme_bw() +
    ggtitle("Frequency distribution of \"risk\" alleles (all 1KG populations combined)")
ggsave(here("plots", "20210127_af_distribution", "20210127_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Allele frequency vs effect size

one = final_df %>% 
  dplyr::filter(HIT_CONTROL == "hit" & POPN == "all") %>% 
  ggplot() +
    geom_point(aes(RISK_AF, OR_OR_BETA, colour = PHENO),
               alpha = 0.2) +
    scale_colour_manual(values = pal_primary) +
    facet_wrap(vars(PHENO), nrow = 2) +
    guides(colour = F, alpha = F) +
    theme_bw() +
    ggtitle("Risk allele frequency vs effect size (OR or beta)")

# Zoom in
two = final_df %>% 
  dplyr::filter(HIT_CONTROL == "hit" & POPN == "all") %>% 
  ggplot() +
    geom_point(aes(RISK_AF, OR_OR_BETA, colour = PHENO),
               alpha = 0.2) +
    scale_colour_manual(values = pal_primary) +
    facet_wrap(vars(PHENO), nrow = 2) +
    guides(colour = F, alpha = F) +
    theme_bw() +
    ggtitle("Risk allele frequency vs effect size (OR or beta)") +
    ylim(0,20)

one
two
ggsave(here("plots", "20210127_af_v_effect_size", "20210127_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_af_v_effect_size", "20210127_hits_zoomed.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Fst

With all populations

Get Fst stats for top SNPs

# Create raw list of variants
hit = here::here("data", "20210128_hits_vcfs")
control = here::here("data", "20210128_rndm_vcfs")
target_dirs = c(hit, control)
names(target_dirs) = c("hit", "control")

# Set seed
#initial_seed = 53
#set.seed(initial_seed)
#seeds = sample(1:1000, size = length(target_dirs))

# Run 
counter = 0
fst_out = lapply(target_dirs, function(target_dir){
  # set counter 
  counter <<- counter + 1
  
  vcf_list_raw <- lapply(trait_levels, function(trait){
    target_file = file.path(target_dir, paste(trait, ".vcf.gz", sep = ""))
    # read in file
    vcf_out <- pegas::read.vcf(target_file)
    
    return(vcf_out)
  })
  
  # Create vector of populations
  populations <- unlist(lapply(rownames(vcf_list_raw[[1]]), function(sample){
    meta$Population[meta$Sample == sample]
  }))
  
  # Generate Fst stats
  fst_out_df <- lapply(vcf_list_raw, function(pheno){
    out = as.data.frame(pegas::Fst(pheno, pop = populations))
    # put rownames into separate column
    out$snp <- rownames(out)
    
    return(out)
  }) %>% 
    # bind into single DF
    dplyr::bind_rows(.id = "phenotype") %>% 
    # remove NA
    tidyr::drop_na()
  
  return(fst_out_df)
}) %>% 
  dplyr::bind_rows(.id = "hit_control")

# Recode phenotype
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels)
fst_out$phenotype = dplyr::recode(fst_out$phenotype, !!!recode_vec)

Write to file

out_dir = here::here("data", "20210127_results")
out_path = file.path(out_dir, paste("20210128_fst", ".csv", sep = ""))

dir.create(out_dir)

readr::write_csv(fst_out, out_path)

Read back in

fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels_verb)

knitr::kable(head(fst_out))
hit_control phenotype Fit Fst Fis snp
hit Height 1 0.0543812 1 rs2710889
hit Height 1 0.2898026 1 rs1240697
hit Height 1 0.0972836 1 rs28401288
hit Height 1 0.0669376 1 rs377599
hit Height 1 0.0274605 1 rs12028979
hit Height 1 0.0888646 1 rs5024246

Fst histograms

one = fst_out %>%
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_primary)  +
    guides(fill = F)

two = fst_out %>%
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_histograms", "20210128_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_histograms", "20210128_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Fst density

Facets
one = fst_out %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_primary) +
    guides(fill = F)


two = fst_out %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_densities", "20210128_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_densities", "20210128_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
Ridges
one = fst_out %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_density_ridges2(mapping = aes(x = Fst, y = phenotype, fill = phenotype),
                         scale = 2) +
    scale_fill_manual(values = pal_primary) +
    ylab(label = NULL) +
    theme_bw() +
    guides(fill = F) +
    scale_y_discrete(expand = expand_scale(add = c(0.2, 2.3)))

two = fst_out %>%
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_density_ridges2(mapping = aes(x = Fst, y = phenotype, fill = phenotype),
                         scale = 2) +
    scale_fill_manual(values = pal_secondary) +
    ylab(label = NULL) +
    theme_bw() +
    guides(fill = F) +
    scale_y_discrete(expand = expand_scale(add = c(0.2, 2.3)))

one
two
ggsave(here("plots", "20210127_ridges", "20210128_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_ridges", "20210128_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Run Kolmogorov-Smirnov Tests

hit_control = unique(fst_out$hit_control)
names(hit_control) = hit_control

ks_out = lapply(hit_control, function(dataset){
  # filter dataset
  target_df = fst_out[fst_out$hit_control == dataset, ]
  # run pairwise KS tests
  ks_out = lapply(trait_levels_verb, function(trait_a){
    out = lapply(trait_levels_verb, function(trait_b){
      results = ks.test(target_df$Fst[target_df$phenotype == trait_a],
                        target_df$Fst[target_df$phenotype == trait_b])
      P = results$p.value
      
      return(P)
    }) %>% 
      dplyr::bind_rows(.id = "test_b")
    
    return(out)
  })  %>% 
    dplyr::bind_rows(.id = "trait")
  
  traits = ks_out$trait
  ks_out$trait <- NULL
  
  rownames(ks_out) = traits
  return(ks_out)
})

# convert to matrix
ks_mat = lapply(ks_out, function(dataset){
  out = as.matrix(dataset)
  
  return(out)
})
# Process for plotting
ks_out_gg = lapply(ks_out, function(dataset){
  out = dataset
  out$A = rownames(dataset)
  out = out %>% 
    pivot_longer(cols = -A, names_to = "B", values_to = "ks_P")
  # convert P-values to -log10
  out$ks_P = -log10(out$ks_P)
  
  return(out)
}) %>% 
  dplyr::bind_rows(.id = "hit_control") %>% 
  dplyr::mutate(across(c("A", "B"),
                       ~factor(.x, levels = trait_levels_verb)))

Plot

heat_hits_all = ks_out_gg %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c() +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")

heat_controls_all = ks_out_gg %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c(option = "magma") +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")

heat_hits_all
heat_controls_all
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_hits_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_controls_all.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Try with same number of SNPs

Strategy: Will it make a difference if we analyse the same number of SNPs for each trait? i.e. is the different number of SNPs for each trait affecting the KS test output?

IBD only has 182 hits, so remove from this part of the analysis.

The trait with the next least number of hits is Intelligence, with `480. So we’ll take a random sample of 40

fst_sample = split(fst_out, f = fst_out$hit_control)
fst_sample = lapply(fst_sample, function(dataset){
  out = split(dataset, f = dataset$phenotype)
  out = lapply(out, function(pheno){
    pheno = pheno %>% 
      dplyr::slice_sample(n = 480)
    
    return(pheno)
  }) %>% 
    dplyr::bind_rows() %>% 
    dplyr::filter(phenotype != "IBD")
}) %>% 
  dplyr::bind_rows()

Histograms

one = fst_sample %>%
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_primary)  +
    guides(fill = F)

two = fst_sample %>%
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_histogram(aes(Fst, fill = phenotype), bins = 100) +
    facet_wrap(~phenotype) +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_histograms", "20210127_hits_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_histograms", "20210127_controls_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Density

one = fst_sample %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_primary) +
    guides(fill = F)

two = fst_sample %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_secondary) +
    guides(fill = F)

one
two
ggsave(here("plots", "20210127_densities", "20210127_hits_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_densities", "20210127_controls_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

KS test

hit_control = unique(fst_sample$hit_control)
names(hit_control) = hit_control

trait_levels_verb_sample = trait_levels_verb[-which(trait_levels_verb == "IBD")]
traits_sample = traits[-which(traits == "ibd")]

ks_sample = lapply(hit_control, function(dataset){
  # filter dataset
  target_df = fst_sample[fst_sample$hit_control == dataset, ]
  # run pairwise KS tests
  ks_out = lapply(trait_levels_verb_sample, function(trait_a){
    out = lapply(trait_levels_verb_sample, function(trait_b){
      results = ks.test(target_df$Fst[target_df$phenotype == trait_a],
                        target_df$Fst[target_df$phenotype == trait_b])
      P = results$p.value
      
      return(P)
    }) %>% 
      dplyr::bind_rows(.id = "test_b")
    
    return(out)
  })  %>% 
    dplyr::bind_rows(.id = "trait")
  
  traits = ks_out$trait_levels_verb_sample
  ks_out$trait <- NULL
  
  rownames(ks_out) = trait_levels_verb_sample
  return(ks_out)
})

Heatmaps

ks_sample_gg = lapply(ks_sample, function(dataset){
  out = dataset
  out$A = rownames(dataset)
  out = out %>% 
    pivot_longer(cols = -A, names_to = "B", values_to = "ks_P")
  # convert P-values to -log10
  out$ks_P = -log10(out$ks_P)
  
  return(out)
}) %>% 
  dplyr::bind_rows(.id = "hit_control") %>% 
  dplyr::mutate(across(c("A", "B"),
                       ~factor(.x, levels = trait_levels_verb_sample)))

Plot

heat_hits_sample = ks_sample_gg %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c() +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")

heat_controls_sample = ks_sample_gg %>% 
  dplyr::filter(hit_control == "control") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = ks_P)) +
    scale_fill_viridis_c(option = "magma") +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "KS-test\n-log(P)")
   
heat_hits_sample
heat_controls_sample
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_hits_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)
ggsave(here("plots", "20210127_ks_heatmaps", "20210127_controls_sample.png"),
       device = "png",
       units = "cm",
       dpi = 400,
       height = 12,
       width = 20)

Compare effects of sampling on P-values

heat_hits_all
heat_hits_sample
heat_controls_all
heat_controls_sample

Try different ways of testing differences

Read in Fst results

fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels_verb[-1])

Run Kolmogorov-Smirnov Tests

From the help pages of ks.test:

The possible values “two.sided”, “less” and “greater” of alternative specify the null hypothesis that the true distribution function of x is equal to, not less than or not greater than the hypothesized distribution function (one-sample case) or the distribution function of y (two-sample case), respectively. This is a comparison of cumulative distribution functions, and the test statistic is the maximum difference in value, with the statistic in the "greater" alternative being D^+ = max[F_x(u) - F_y(u)]. Thus in the two-sample case alternative = "greater" includes distributions for which x is stochastically smaller than y (the CDF of x lies above and hence to the left of that for y), in contrast to t.test or wilcox.test.

ks_out = split(fst_out, f = fst_out$hit_control)
ks_out = lapply(ks_out, function(dataset){
  split_data = split(dataset, f = dataset$phenotype)
  # run pairwise KS tests
  ks_out = lapply(split_data, function(trait_a){
    out = lapply(split_data, function(trait_b){
      # "two.sided"
      results_two_sided = ks.test(trait_a$Fst,
                                  trait_b$Fst,
                                  alternative = "two.sided")      
      # "less" test
      results_less = ks.test(trait_a$Fst,
                             trait_b$Fst,
                             alternative = "less")
      # "greater" test
      results_greater = ks.test(trait_a$Fst,
                                trait_b$Fst,
                                alternative = "greater")
      # Mann-Whitney 
      results_mw = wilcox.test(trait_a$Fst,
                               trait_b$Fst,
                               alternative = "two.sided")
      # bind into DF
      df_out = c(results_two_sided[["statistic"]],
                 "P_TWO_SIDED" = results_two_sided[["p.value"]],
                 results_less[["statistic"]],
                 "P_LESS" = results_less[["p.value"]],
                 results_greater[["statistic"]],
                 "P_GREATER" = results_greater[["p.value"]],
                 results_mw[["statistic"]],
                 "P_MW" = results_mw[["p.value"]])

      return(df_out)
      return(results_mw)
    }) %>% 
      dplyr::bind_rows(.id = "B")
  }) %>% 
    dplyr::bind_rows(.id = "A")
}) %>% 
  dplyr::bind_rows(.id = "HIT_CONTROL") %>% 
  dplyr::mutate(across(c("A", "B"),
                       ~factor(.x, levels = trait_levels_verb)))

Plot KS-test D

Show correlation

plt = ks_out %>% 
  ggplot() +
    geom_point(aes(`D^+`, `D^-`, colour = HIT_CONTROL,
                   text = paste("Trait A: ", A,
                                "<br>Trait B: ", B))) +
    theme_bw() +
    coord_fixed()
Ignoring unknown aesthetics: text
ggplotly(plt)

NA
ggplotly(plt) %>%
  export(file = here::here("plots", "20210129_KS-D.svg"),
         selenium = RSelenium::rsDriver(browser = "firefox"))

Plot MW

heat_hits_sample = ks_out %>% 
  dplyr::filter(CASE_CONTROL == "case") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = -log10(P_MW))) +
    scale_fill_viridis_c() +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "MW-test\n-log(W)")

heat_controls_sample = ks_out %>% 
  dplyr::filter(CASE_CONTROL == "control") %>% 
  ggplot() +
    geom_tile(aes(A, B, fill = -log10(P_MW))) +
    scale_fill_viridis_c(option = "magma") +
    coord_fixed() +
    xlab(NULL) +
    ylab(NULL) +
    labs(fill = "MW-test\n-log(W)")
   
heat_hits_sample

heat_controls_sample

Calculate Fst for 100k random SNPs to compare to these distributions

Pull random lines from 1kg alfreq file

# On cluster 

list_in=../big_data/20210125_alfreqs_all_binned/all.afreq
list_out=data/20210129_random_100k_snps.list
ref=../refs/hs37d5.fa.gz
in_vcf=../vcfs/1kg_all.vcf.gz
out_vcf=../vcfs/1kg_100k_rndm.vcf.gz

conda activate fst_env_rhel 

# create function to get random seed
get_seeded_random()
{
  seed="$1"
  openssl enc -aes-256-ctr -pass pass:"$seed" -nosalt \
    </dev/zero 2>/dev/null
}

# make list
awk '{print $2}' $list_in |\
  tail -n+2 |\
  shuf -n 100000 \
  --random-source=<(get_seeded_random 454) \
    > $list_out
    
# extract from 1KG
gatk SelectVariants \
  -R $ref \
  -V $in_vcf \
  --keep-ids $list_out \
  -O $out_vcf

Run pegas to get Fst

# On cluster
library(here)
source(here::here("code", "scripts", "source.R"))

# Set variables
in_vcf=here::here("..", "vcfs", "1kg_100k_rndm.vcf.gz")
samples_file = here::here("data", "20130606_sample_info.xlsx")
out_file=here::here("data", "20210129_100k_rndm_fst.txt")

# Read in `meta` file
meta = readxl::read_xlsx(samples_file,
                         sheet = "Sample Info") %>%
  dplyr::select(Sample, Population, Gender)

# Read VCF 
vcf_out <- pegas::read.vcf(in_vcf, to = 100000)

# Create vector of populations
populations = unlist(lapply(rownames(vcf_out), function(sample){
  meta$Population[meta$Sample == sample]
}))

# Generate Fst stats
fst_out <- as.data.frame(pegas::Fst(vcf_out, pop = populations))
fst_out$snp <- rownames(fst_out)
# remove NAs
fst_out  = fst_out %>% 
  tidyr::drop_na() 
# 99682 remaining
  
# Set phenotype
fst_out$phenotype <- "Random"

# Save file
readr::write_tsv(fst_out, out_file)

Read in

fst_random = readr::read_tsv(here::here("data", "20210129_100k_rndm_fst.txt")) %>% 
  dplyr::mutate(hit_control = "hit")

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  Fit = col_double(),
  Fst = col_double(),
  Fis = col_double(),
  snp = col_character(),
  phenotype = col_character()
)

Plot

Fst density

Facets
fst_out %>% 
  dplyr::filter(hit_control == "hit") %>% 
  ggplot(aes(Fst, fill = phenotype)) +
    geom_density() +
    labs(fill = "Phenotype") +
    facet_wrap(~phenotype) +
    ylab("Density") +
    theme_bw() +
    scale_fill_manual(values = pal_primary) +
    guides(fill = F)

Ridges

Final

Get median Fst and 0.9 percentile

Read in data

# Read in data
fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))

── Column specification ───────────────────────────────────────────────────────────
cols(
  hit_control = col_character(),
  phenotype = col_character(),
  Fit = col_double(),
  Fst = col_double(),
  Fis = col_double(),
  snp = col_character()
)
fst_out$phenotype <- factor(fst_out$phenotype, levels = trait_levels_verb)

Median and 0.9 percentile

From the help page of wilcoxon.test:

Note that in the two-sample case the estimator for the difference in location parameters does not estimate the difference in medians (a common misconception) but rather the median of the difference between a sample from x and a sample from y.

ks.test(fst_list$hit$Height$Fst, fst_list$hit$`Educational attainment`$Fst)
p-value will be approximate in the presence of ties

    Two-sample Kolmogorov-Smirnov test

data:  fst_list$hit$Height$Fst and fst_list$hit$`Educational attainment`$Fst
D = 0.087524, p-value = 1.461e-06
alternative hypothesis: two-sided

Plot

mw_gg %>% 
  ggplot() +
    geom_col(aes(PHENO, -log10(P_MW), fill = PHENO)) +
    facet_wrap(~HIT_CONTROL) + 
    scale_fill_manual(values = pal_primary) +
    theme_bw() +
    ggtitle("Mann-Whitney p-values")

mw_gg %>% 
  ggplot() +
    geom_col(aes(PHENO, -log10(P_KS), fill = PHENO)) +
    facet_wrap(~HIT_CONTROL) + 
    scale_fill_manual(values = pal_primary) +
    theme_bw() +
    ggtitle("KS-test p-values")

20210201

Final plots

Fst density for hits

Read in data

# read
fst_out = readr::read_csv(here::here("data", "20210127_results/20210128_fst.csv"))

── Column specification ──────────────────────────────────────────────────────
cols(
  hit_control = col_character(),
  phenotype = col_character(),
  Fit = col_double(),
  Fst = col_double(),
  Fis = col_double(),
  snp = col_character()
)

LS0tCnRpdGxlOiAiRnN0IHZhcmlhdGlvbiBhY3Jvc3MgaHVtYW4gdHJhaXRzIgphdXRob3I6ICJJYW4gQnJldHRlbGwiCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSlgJwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKI291dHB1dDoKIyAgaHRtbF9kb2N1bWVudDoKIyAgICB0b2M6IHRydWUKIyAgICB0b2NfZmxvYXQ6IHRydWUKIyAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKIyAgICBrZWVwX21kOiB0cnVlCiMgICAgcGFuZG9jX2FyZ3M6IC0tbHVhLWZpbHRlcj1jb2xvci10ZXh0Lmx1YQojICAgIGhpZ2hsaWdodDogcHlnbWVudHMgIAotLS0KCiMgU2V0dXAKCiogW1dvcmtpbmcgZGlyZWN0b3J5XXtjb2xvcj0iIzRmMDk0MyJ9IG9uIEVCSSBDbHVzdGVyOiBgL2hwcy9yZXNlYXJjaDEvYmlybmV5L3VzZXJzL2lhbi9obW5fZnN0YAoqIFtHaXRIdWIgcmVwb3NpdG9yeV17Y29sb3I9IiM0ZjA5NDMifTogPGh0dHBzOi8vZ2l0aHViLmNvbS9icmV0dGVsbGViaS9odW1hbl90cmFpdHNfZnN0PgoKIyMgYGNvbmRhYCBlbnYgb24gY2x1c3RlcgoKYGBge3IsIGVuZ2luZT0nYmFzaCcsIGV2YWwgPSBGfQojIENyZWF0ZSBlbnYgb24gY2x1c3RlciB3aXRoIG1hbWJhCm1hbWJhIGNyZWF0ZSAteSBcCiAgLW4gZnN0X2Vudl9yaGVsIFwKICAtYyBiaW9jb25kYSBnYXRrNApjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwKbWFtYmEgaW5zdGFsbCBiY2Z0b29scyBwbGluazIgci1iYXNlIHItZXNzZW50aWFscyByLXRpZHl2ZXJzZSByLXVuaXRzIGxpYmdkYWwgci1zZgojIEV4cG9ydApjb25kYSBlbnYgZXhwb3J0IFwKICAtLW5vLWJ1aWxkcyBcCiAgLWYgZW52cy9mc3RfZW52X3JoZWwueW1sCiMgQWN0aXZhdGUKY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsCmBgYAoKIyMgYHJlbnZgCgpgYGB7ciwgZXZhbCA9IEZ9CiMgRXhwb3J0IGVudiAodG8gcmVudi5sb2NrIGZpbGUpCnJlbnY6OmluaXQoKQojIFRvIGluc3RhbGwgcGFja2FnZXMgb24gbmV3IHN5c3RlbSwgb3IgJ2FjdGl2YXRlJyB0aGUgZW52OiAKcmVudjo6cmVzdG9yZSgpCmBgYAoKIyMgU291cmNlIGxpYnJhcmllcywgZnVuY3Rpb25zIGFuZCBwbG90dGluZyBwYXJhbWV0ZXJzCgpgYGB7ciwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpsaWJyYXJ5KGhlcmUpCgpzb3VyY2UoaGVyZTo6aGVyZSgiY29kZSIsICJzY3JpcHRzIiwgInNvdXJjZS5SIikpCmBgYAoKIyMgRG93bmxvYWQgMUtHIGRhdGEKCiMjIyBEb3dubG9hZCBmcm9tIEZUUAoKYGBge2Jhc2gsIGV2YWwgPSBGfQp3Z2V0IFwKICAtciAtcCAtayBcCiAgLS1uby1wYXJlbnQgXAogIC1jdXQtZGlycz01IFwKICBmdHA6Ly9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL3ZvbDEvZnRwL3JlbGVhc2UvMjAxMzA1MDIvCmBgYAoKIyMjIFB1dCBmaWxlbmFtZXMgaW50byBsaXN0CgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbCA9IEZ9CmZpbmQgdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHIqLnZjZi5neiBcCiAgPiBodW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CmBgYAoKIyMjIE1lcmdlIFZDRnMKCmBgYHtyLCBlbmdpbmU9J2Jhc2gnLCBldmFsID0gRn0KamF2YSAtamFyIC9uZnMvc29mdHdhcmUvYmlybmV5L3BpY2FyZC0yLjkuMC9waWNhcmQuamFyIE1lcmdlVmNmcyBcCiAgST1odW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0IFwKICBPPXZjZnMvMWtnX2FsbC52Y2YuZ3oKIyBFeGNlcHRpb24gaW4gdGhyZWFkICJtYWluIiBqYXZhLmxhbmcuSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uOiBUaGUgY29udGlnIGVudHJpZXMgaW4gaW5wdXQgZmlsZSAvaHBzL3Jlc2VhcmNoMS9iaXJuZXkvdXNlcnMvaWFuL3JhY19oeXAvdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHJNVC5waGFzZTNfY2FsbG1vbS12MF80LjIwMTMwNTAyLmdlbm90eXBlcy52Y2YuZ3ogYXJlIG5vdCBjb21wYXRpYmxlIHdpdGggdGhlIG90aGVycy4KCiMgU28gcmVtb3ZlIHRoYXQgb25lIGZyb20gbGlzdCBhYm92ZQpzZWQgLWkgJy9NVC9kJyBodW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CgojIHJ1biBNZXJnZVZDRnMgYWdhaW4KamF2YSAtamFyIC9uZnMvc29mdHdhcmUvYmlybmV5L3BpY2FyZC0yLjkuMC9waWNhcmQuamFyIE1lcmdlVmNmcyBcCiAgST1odW1hbl90cmFpdHNfZnN0L2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0IFwKICBPPXZjZnMvMWtnX2FsbC52Y2YuZ3oKICAKIyBFeGNlcHRpb24gaW4gdGhyZWFkICJtYWluIiBqYXZhLmxhbmcuSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uOiBUaGUgY29udGlnIGVudHJpZXMgaW4gaW5wdXQgZmlsZSAvaHBzL3Jlc2VhcmNoMS9iaXJuZXkvdXNlcnMvaWFuL3JhY19oeXAvdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHJZLnBoYXNlM19pbnRlZ3JhdGVkX3YyYS4yMDEzMDUwMi5nZW5vdHlwZXMudmNmLmd6IGFyZSBub3QgY29tcGF0aWJsZSB3aXRoIHRoZSBvdGhlcnMuCnNlZCAtaSAnL2NoclkvZCcgaHVtYW5fdHJhaXRzX2ZzdC9kYXRhLzIwMjAwMjA1X3ZjZnMubGlzdAoKIyBydW4gTWVyZ2VWQ0ZzIGFnYWluCmphdmEgLWphciAvbmZzL3NvZnR3YXJlL2Jpcm5leS9waWNhcmQtMi45LjAvcGljYXJkLmphciBNZXJnZVZjZnMgXAogIEk9aHVtYW5fdHJhaXRzX2ZzdC9kYXRhLzIwMjAwMjA1X3ZjZnMubGlzdCBcCiAgTz12Y2ZzLzFrZ19hbGwudmNmLmd6CiMgU1VDQ0VTUwpgYGAKCiMjIE9idGFpbiBHV0FTIGRhdGEgZnJvbSB0aGUgR1dBUyBDYXRhbG9nIDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcz4KCiMjIyBQdWxsIGRhdGEgZm9yIGVhY2ggdHJhaXQKClsqKk5PVEUqKl17Y29sb3I9InJlZCJ9OiBVbmNoZWNrIGBJbmNsdWRlIGNoaWxkIHRyYWl0IGRhdGFgIGJlZm9yZSBkb3dubG9hZGluZy4KCkFsbCBkb2N1bWVudHMgZG93bmxvYWRlZCB2aWEgJ0Rvd25sb2FkIENhdGFsb2cgZGF0YScgbGluaywgdGhlbiBjb2xsYXRlZCBhbmQgc2F2ZWQgaGVyZTogYGRhdGEvMjAyMTAxMjJfZ3dhc19jYXRhbG9nLnhsc3hgCgojIyMjIEhlaWdodAoKKiBoZWlnaHQ6IDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy9lZm90cmFpdHMvRUZPXzAwMDQzMzk+CiAgLSAqKjQ5MTIgU05QcyoqIGZyb20gKio1MSBzdHVkaWVzKioKICAKIyMjIyBCTUkKCiogYm1pOiA8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA0MzQwPgogIC0gKio3NTczIFNOUHMqKiBmcm9tICoqMTU1IHN0dWRpZXMqKgogIAojIyMjIEVkdWNhdGlvbmFsIGF0dGFpbm1lbnQKCiogc2VsZiByZXBvcnRlZCBlZHVjYXRpb25hbCBhdHRhaW5tZW50OiA8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA0Nzg0PgogIC0gKiozOTg5IFNOUHMqKiBmcm9tICoqMjQgc3R1ZGllcyoqCiAgCiMjIyMgSW50ZWxsaWdlbmNlCgoqIGludGVsbGlnZW5jZTogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwNDMzNz4KICAtICoqMjk2NyBTTlBzKiogZnJvbSAqKjI3IHN0dWRpZXMqKgogIAojIyMjIElCRAoKKiBpbmZsYW1tYXRvcnkgYm93ZWwgZGlzZWFzZTogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwMzc2Nz4KICAtICoqNTM2IFNOUHMqKiBmcm9tICoqMzQgc3R1ZGllcyoqCgojIyMjIFBpZ21lbnRhdGlvbgoKKiBza2luIHBpZ21lbnRhdGlvbjogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwMzc4ND4KICAtICoqMTAyIFNOUHMqKiBmcm9tICoqNiBzdHVkaWVzKioKCiogc2tpbiBwaWdtZW50YXRpb24gbWVhc3VyZW1lbnQ6IDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy9lZm90cmFpdHMvRUZPXzAwMDcwMDk+CiAgLSAqKjIzMyBTTlBzKiogZnJvbSAqKjkgc3R1ZGllcyoqCgoqIGV5ZSBjb2xvcjogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwMzk0OT4KICAtICoqNzcgU05QcyoqIGZyb20gKioxMyBzdHVkaWVzKioKICAKKiBleWUgY29sb3VyIG1lYXN1cmVtZW50Ogo8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA5NzY0PgogIC0gKioyMDIgU05QcyoqIGZyb20gKio5IHN0dWRpZXMqKgogIAoqIGhhaXIgY29sb3I6IDxodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy9lZm90cmFpdHMvRUZPXzAwMDM5MjQ+CiAgLSAqKjQyNCBTTlBzKiogZnJvbSAqKjE4IHN0dWRpZXMqKgogIAoqIGhhaXIgY29sb3VyIG1lYXN1cmVtZW50OiA8aHR0cHM6Ly93d3cuZWJpLmFjLnVrL2d3YXMvZWZvdHJhaXRzL0VGT18wMDA3ODIyPgogIC0gKio1NDEgU05QcyoqIGZyb20gKio2IHN0dWRpZXMqKgoKIyMjIFJlYWQgaW50byBsaXN0CgpgYGB7ciwgd2FybmluZ3MgPSBGfQpmaWxlX2luID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyMl9nd2FzX2NhdGFsb2cueGxzeCIpCiMgQ3JlYXRlIHZlY3RvciBvZiB0cmFpdHMKdHJhaXRzID0gYygiaGVpIiwgImJtaSIsICJlZHUiLCAiaW50IiwgImliZCIsICJwaWciKQpuYW1lcyh0cmFpdHMpID0gdHJhaXRzCiMgQXNzaWduIHNoZWV0cyB0byB0cmFpdHMKc2hlZXRzIDwtIHNlcSgxOjExKQpuYW1lcyhzaGVldHMpIDwtIGMoImhlaSIsICJibWkiLCAiZWR1IiwgImludCIsICJpYmQiLCByZXAoInBpZyIsIDYpKQoKIyBnZXQgc2hlZXRzCnNoZWV0X25hbWVzIDwtIHJlYWR4bDo6ZXhjZWxfc2hlZXRzKGZpbGVfaW4pCgojIENyZWF0ZSBmdW5jdGlvbiB0byByZWFkIGluIGRhdGEKcmVhZF9jYXRhbG9nX2RhdGEgPC0gZnVuY3Rpb24ocGF0aCwgdGFyZ2V0X3NoZWV0KXsKICAjIFJlYWQgaW4gZGF0YQogIG91dCA9IHJlYWR4bDo6cmVhZF94bHN4KHBhdGgsIHNoZWV0ID0gdGFyZ2V0X3NoZWV0KSAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KENIUiA9IENIUl9JRCwgCiAgICAgICAgICAgICAgICAgIFBPUyA9IENIUl9QT1MsIAogICAgICAgICAgICAgICAgICBTTlBfQUwgPSBgU1RST05HRVNUIFNOUC1SSVNLIEFMTEVMRWAsIAogICAgICAgICAgICAgICAgICBQID0gYFAtVkFMVUVgLCAKICAgICAgICAgICAgICAgICAgT1JfT1JfQkVUQSA9IGBPUiBvciBCRVRBYCwgCiAgICAgICAgICAgICAgICAgIE1BUFBFRF9UUkFJVCwKICAgICAgICAgICAgICAgICAgU1RVRFkgPSBgU1RVRFkgQUNDRVNTSU9OYCwKICAgICAgICAgICAgICAgICAgU0FNUExFID0gYElOSVRJQUwgU0FNUExFIFNJWkVgKSAlPiUgCiAgICAjIFNwbGl0IFNOUCBhbmQgcmlzayBhbGxlbGUgaW50byBzZXBhcmF0ZSBjb2x1bW5zCiAgICBkcGx5cjo6bXV0YXRlKFRPUF9TTlAgPSBzdHJpbmdyOjpzdHJfc3BsaXQoU05QX0FMLCAiLSIsIHNpbXBsaWZ5ID0gVClbLCAxXSwKICAgICAgICAgICAgICAgICAgUklTS19BTExFTEUgPSBzdHJpbmdyOjpzdHJfc3BsaXQoU05QX0FMLCAiLSIsIHNpbXBsaWZ5ID0gVClbLCAyXSkgJT4lIAogICAgIyBSZW9yZGVyIGFuZCBzZWxlY3QKICAgIGRwbHlyOjpzZWxlY3QoQ0hSLCBQT1MsIFRPUF9TTlAsIFJJU0tfQUxMRUxFLCBQLCBPUl9PUl9CRVRBLCBNQVBQRURfVFJBSVQsIFNUVURZLCBTQU1QTEUpCiAgIyBDaGFuZ2UgdmFyaWFibGVzIHRvIHNwZWNpZmljIHR5cGVzCiAgb3V0JENIUiA8LSBhcy5pbnRlZ2VyKG91dCRDSFIpCiAgb3V0JFBPUyA8LSBhcy5udW1lcmljKG91dCRQT1MpCiAgb3V0JFAgPC0gYXMubnVtZXJpYyhvdXQkUCkKICAjIHJldHVybiBERgogIHJldHVybihvdXQpCn0KCiMgUmVhZCBpbiBkYXRhCmNvdW50ZXIgPC0gMApkYXRhX2xpc3QgPSBsYXBwbHkodHJhaXRzLCBmdW5jdGlvbih0cmFpdCl7CiAgIyBzZXQgY291bnRlciAKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgc2V0IHRhcmdldCBmaWxlCiAgdGFyZ2V0X2ZpbGUgPSBmaWxlX2luCiAgIyBnZXQgdGFyZ2V0IHNoZWV0CiAgdGFyZ2V0X3NoZWV0ID0gc2hlZXRzW25hbWVzKHNoZWV0cykgPT0gdHJhaXRdCiAgbGVuZ3RoKHRhcmdldF9zaGVldCkKICAjIHJlYWQgaW4gcGlnbWVudGF0aW9uIGRhdGEgZnJvbSBtdWx0aXBsZSBzaGVldHMgYW5kIGJpbmQgaW50byBzaW5nbGUgREYKICBpZiAobGVuZ3RoKHRhcmdldF9zaGVldCkgPiAxKXsKICAgICMgbG9vcCBvdmVyIGVhY2ggc2hlZXQKICAgIGRmIDwtIGxhcHBseSh0YXJnZXRfc2hlZXQsIGZ1bmN0aW9uKHNoZWV0KXsKICAgICAgb3V0IDwtIHJlYWRfY2F0YWxvZ19kYXRhKHRhcmdldF9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3NoZWV0ID0gc2hlZXQpCiAgICB9KQogICAgIyBzZXQgbmFtZSBvZiBlYWNoIERGIHRvIG5hbWUgb2Ygc2hlZXQgKHJlcGxhY2luZyBzcGFjZXMgd2l0aCB1bmRlcnNjb3JlcykKICAgIG5hbWVzKGRmKSA9IHNoZWV0X25hbWVzW3RhcmdldF9zaGVldF0gJT4lIAogICAgICBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwoIiAiLCAiXyIpCiAgICAjIGJpbmQgREZzIGludG8gc2luZ2xlIERGCiAgICBkZiA8LSBkcGx5cjo6YmluZF9yb3dzKGRmLCAuaWQgPSAiUElHX1BIRU5PIikKICB9IAogIGVsc2UgewogICAgIyByZWFkIGluIG90aGVyIGRhdGEKICAgIGRmIDwtIHJlYWRfY2F0YWxvZ19kYXRhKHRhcmdldF9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X3NoZWV0ID0gdGFyZ2V0X3NoZWV0KQogIH0KICAjIFNldCBQSEVOTyBjb2x1bW4KICBkZiRQSEVOTyA8LSBmYWN0b3IodHJhaXQsIGxldmVscyA9IHRyYWl0X2xldmVscykKICAjIFJlY29kZSBQSEVOTwogIGRmJFBIRU5PID0gZHBseXI6OnJlY29kZShkZiRQSEVOTywgISEhcmVjb2RlX3ZlYykKICAjIENyZWF0ZSBsaXN0CiAgb3V0ID0gbGlzdCgpCiAgIyBSZXR1cm4gREYgYXMgInJhdyIKICBvdXRbWyJyYXciXV0gPSBkZgogIAogIHJldHVybihvdXQpCn0pCgojIEhvdyBtYW55IFNOUHMgaW4gcmF3IGRhdGEKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24oeCkgbnJvdyh4W1sicmF3Il1dKSkKCiMgQ2xlYW4gZGF0YQpkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgZGZfY2xlYW4gPSBwaGVub1tbInJhdyJdXQogICMgUmVtb3ZlIHJvd3Mgd2l0aCBwLXZhbHVlIG9mIDAgKG9ubHkgMzIgb2YgdGhlbSwgYXNzb2NpYXRlZCB3aXRoIHN1bnRhbikKICBkZl9jbGVhbiA9IGRmX2NsZWFuW2RmX2NsZWFuJFAgIT0gMCwgXQogICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBDSFIKICBkZl9jbGVhbiA9IGRmX2NsZWFuWyFpcy5uYShkZl9jbGVhbiRDSFIpLCBdCiAgIyBSZW1vdmUgZHVwbGljYXRlcwogICMjIEZpbmQgU05QcyB0aGF0IGFyZSBkdXBsaWNhdGVkCiAgZHVwZXMgPSB1bmlxdWUoZGZfY2xlYW4kVE9QX1NOUFtkdXBsaWNhdGVkKGRmX2NsZWFuJFRPUF9TTlApIHwgZHVwbGljYXRlZChkZl9jbGVhbiRUT1BfU05QLCBmcm9tTGFzdCA9IFQpXSkKICAjIyBTZWxlY3Qgb25seSAxIFNOUCBmcm9tIGVhY2ggc2V0IG9mIGR1cGxpY2F0ZWQgU05QcwogIGR1cGVfZmlsdCA9IGxhcHBseShkdXBlcywgZnVuY3Rpb24oZHVwZSl7CiAgICAjIFRha2UgdGhlIG9uZSB3aXRoIHRoZSBsb3dlc3QgUC12YWx1ZQogICAgbWluX3AgPSBtaW4oZGZfY2xlYW4kUFtkZl9jbGVhbiRUT1BfU05QID09IGR1cGVdKQogICAgb3V0ID0gZGZfY2xlYW5bZGZfY2xlYW4kVE9QX1NOUCA9PSBkdXBlICYgZGZfY2xlYW4kUCA9PSBtaW5fcCwgXQogICAgIyBJZiB0aGVyZSBhcmUgc3RpbGwgZHVwbGljYXRlcyBkdWUgdG8gaGF2aW5nIGVxdWFsIFAsIHRha2UgdGhlIG9uZSB3aXRoIHRoZSBsYXJnZXN0IGVmZmVjdCBzaXplCiAgICBpZiAobnJvdyhvdXQpID4gMSApewogICAgICBvdXQgPSBvdXRbd2hpY2gubWF4KG91dCRPUl9PUl9CRVRBKSwgXQogICAgfQogICAgcmV0dXJuKG91dCkKICB9KQogICMgQmluZCBsaXN0IGludG8gREYKICBkdXBlX2ZpbHQgPSBkcGx5cjo6YmluZF9yb3dzKGR1cGVfZmlsdCkKICAjIEV4dHJhY3Qgbm9uLWR1cGxpY2F0ZWQgcm93cwogIG5vbl9kdXBlID0gZGZfY2xlYW5bIWR1cGxpY2F0ZWQoZGZfY2xlYW4kVE9QX1NOUCkgJiAhZHVwbGljYXRlZChkZl9jbGVhbiRUT1BfU05QLCBmcm9tTGFzdCA9IFQpLCBdCiAgIyBCaW5kIG5vbi1kdXBsaWNhdGVkIHJvd3Mgd2l0aCBmaWx0ZXJlZCBkdXBsaWNhdGVzCiAgZGZfY2xlYW4gPSByYmluZChub25fZHVwZSwgZHVwZV9maWx0KSAKICAjIEFkZCB0byBsaXN0CiAgcGhlbm9bWyJjbGVhbiJdXSA9IGRmX2NsZWFuCiAgCiAgcmV0dXJuKHBoZW5vKQp9KQoKIyBOZXcgU05QIGNvdW50CmxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHgpIG5yb3coeFtbImNsZWFuIl1dKSkKYGBgCgpgYGB7ciwgcmVzdWx0cyA9ICdhc2lzJ30KbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogIGtuaXRyOjprYWJsZShoZWFkKHBoZW5vW1siY2xlYW4iXV0pKQp9KQpgYGAKCkdldCB1bmlxdWUgbWFwcGVkIHRyYWl0cwoKYGBge3J9CmxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICB1bmlxdWUocGhlbm8kY2xlYW4kTUFQUEVEX1RSQUlUKQp9KQpgYGAKCkFyZSBhbnkgb2YgdGhlIE9SX09SX0JFVEFzIG5lZ2F0aXZlPyAKCmBgYHtyfQphbnkodW5saXN0KGxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKSB7CiAgYW55KHBoZW5vJGNsZWFuJE9SX09SX0JFVEEgPCAwLG5hLnJtID0gVCkKfSkpKQpgYGAKClRoaXMgbXVzdCBtZWFucyB0aGF0IHRoZSBgUklTS19BTExFTEVgIGFsd2F5cyBhZmZlY3RzIHRoZSB0cmFpdCBwb3NpdGl2ZWx5LiAKCkJ1dCBmb3Igd2hhdCBwcm9wb3J0aW9uIG9mIFNOUHMgaXMgdGhlIGBSSVNLX0FMTEVMRWAgbm90IHN0YXRlZD8KCmBgYHtyfQpsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubykgewogIGxlbmd0aCh3aGljaChwaGVubyRjbGVhbiRSSVNLX0FMTEVMRSA9PSAiPyIpKSAvIG5yb3cocGhlbm8kY2xlYW4pCn0pCmBgYAoKWyoqTk9URSoqXXtjb2xvcj0icmVkIn06IEluIGNhc2VzIHdoZXJlIGBSSVNLX0FMTEVMRWAgaXNuJ3QgcHJvdmlkZWQsIHRyZWF0IHRoZSBgQUxUYCBhbGxlbGUgYXMgYFJJU0tfQUxMRUxFYC4KCiMjIyBHZW5lcmF0ZSBNYW5oYXR0YW4gcGxvdHMKClBsb3QKCmBgYHtyLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICJoaWRlIn0KY291bnRlciA9IDAKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm9fZGYpewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogIGRmID0gcGhlbm9fZGZbWyJjbGVhbiJdXQogIHRyYWl0ID0gdW5pcXVlKGRmJFBIRU5PKQogICMgR2V0IHRpdGxlCiAgdGl0bGUgPC0gcGFzdGUodHJhaXQsICJcbiIsICJTTlAgY291bnQ6IiwgbnJvdyhkZikpCiAgIyBQbG90CiAgZ2V0X21hbihkZiwgdHJhaXQgPSB0cmFpdCwgdGl0bGUgPSB0aXRsZSwgY2hyID0gIkNIUiIsIGJwID0gIlBPUyIsIHNucCA9ICJUT1BfU05QIiwgcCA9ICJQIikKfSkKYGBgCgpTYXZlCgpgYGB7ciwgZXZhbCA9IEZ9Cm91dHB1dF9kaXIgPSBoZXJlOjpoZXJlKCJwbG90cyIsICIyMDIxMDEyMl9tYW5oYXR0YW5fYWxsX3NucHMiKQoKY291bnRlciA9IDAKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm9fZGYpewoKICAjIHNldCBjb3VudGVyCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBkZiA9IHBoZW5vX2RmW1siY2xlYW4iXV0KICB0cmFpdCA9IHVuaXF1ZShkZiRQSEVOTykKICAjIEdldCB0aXRsZQogIHRpdGxlIDwtIHBhc3RlKHRyYWl0LCAiXG4iLCAiU05QIGNvdW50OiIsIG5yb3coZGYpKQogICMgU2V0IGZpbGUgbmFtZSB0byBzYXZlCiAgZmlsZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLAogICAgICAgICAgICAgICAgICAgcGFzdGUoIm1hbmhhdHRhbl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICIuc3ZnIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSkKICAjIFNldCB1cCBncmFwaGljcyBkZXZpY2UKICBzdmcoZmlsZSwKICAgICAgd2lkdGggPSAxMCwKICAgICAgaGVpZ2h0ID0gNikgIAogIAogICMgUGxvdAogIGdldF9tYW4oZGYsIHRyYWl0ID0gdHJhaXQsIGNociA9ICJDSFIiLCBicCA9ICJQT1MiLCBzbnAgPSAiVE9QX1NOUCIsIHAgPSAiUCIpCiAgCiAgZGV2Lm9mZigpCn0pCmBgYAoKIyMjIyBDcmVhdGUgbGlzdCBvZiB0YXJnZXQgU05QcyB0byBleHRyYWN0IGZyb20gMUtHCgpgYGB7ciwgZXZhbCA9IEYsIHdhcm5pbmcgPSBGLCByZXN1bHRzID0gImhpZGUifQoKZGVzdF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTIyX3NucF9oaXRfbGlzdHMiKQoKIyBNYWtlIGRpcmVjdG9yeQpkaXIuY3JlYXRlKGRlc3RfZGlyKQogIAojIEp1c3QgU05QcyBmb3IgZXh0cmFjdGluZyBmcm9tIDFLRwpjb3VudGVyIDwtIDAKbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogIGRmID0gcGhlbm9bWyJjbGVhbiJdXQogICMgU2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgU2V0IGZpbGUgYmFzZW5hbWUKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICBmaWxlbmFtZSA9IHBhc3RlKHRyYWl0LCAiLmxpc3QiLCBzZXAgPSAiIikKICAjIFdyaXRlIFNOUHMgdG8gZmlsZQogIHJlYWRyOjp3cml0ZV9saW5lcyhkZiRUT1BfU05QLCBmaWxlLnBhdGgoZGVzdF9kaXIsIGZpbGVuYW1lKSkKfSkKCiMgU05QcyBhbmQgUC12YWx1ZXMgd2l0aCBoZWFkZXIgZm9yIGNsdW1waW5nIHdpdGggUGxpbmsKY291bnRlciA8LSAwCmxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICBkZiA9IHBoZW5vW1siY2xlYW4iXV0KICAjIFNldCBjb3VudGVyCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICAjIFNldCBmaWxlIGJhc2VuYW1lCiAgdHJhaXQgPSBuYW1lcyhkYXRhX2xpc3QpW2NvdW50ZXJdCiAgZmlsZW5hbWUgPSBwYXN0ZSh0cmFpdCwgIl93aXRoX1AudHh0Iiwgc2VwID0gIiIpCiAgIyBXcml0ZSBTTlBzIHRvIGZpbGUKICBkZiAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KFNOUCA9IFRPUF9TTlAsIFApICU+JSAKICAgIHJlYWRyOjp3cml0ZV90c3YoZmlsZS5wYXRoKGRlc3RfZGlyLCBmaWxlbmFtZSkpCn0pCmBgYAoKIyMgRmlsdGVyIDFLRyBWQ0YgZm9yIHRhcmdldCBTTlBzCgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbCA9IEZ9Cgp0cmFpdHM9JChlY2hvIGhlaSBibWkgZWR1IGludCBpYmQgcGlnKQpyZWY9Li4vcmVmcy9oczM3ZDUuZmEuZ3oKaW5fdmNmPS4uL3ZjZnMvMWtnX2FsbC52Y2YuZ3oKc25wc19kaXI9ZGF0YS8yMDIxMDEyMl9zbnBfaGl0X2xpc3RzCm91dF9kaXI9ZGF0YS8yMDIxMDEyNV9zbnBfaGl0c19maWx0ZXJlZAoKbWtkaXIgLXAgJG91dF9kaXIKCmZvciB0cmFpdCBpbiAkKGVjaG8gJHRyYWl0cyApOyBkbwogIGJzdWIgXAogICAgLU0gMTAwMDAgXAogICAgLW8gLi4vbG9nLzIwMjEwMTIyX2V4dHJhY3Rfc25wc18kdHJhaXQub3V0IFwKICAgIC1lIC4uL2xvZy8yMDIxMDEyMl9leHRyYWN0X3NucHNfJHRyYWl0LmVyciBcCiAgICAiIiIKICAgIGNvbmRhIGFjdGl2YXRlIGZzdF9lbnZfcmhlbCA7CiAgICBnYXRrIFNlbGVjdFZhcmlhbnRzIFwKICAgICAgLVIgJHJlZiBcCiAgICAgIC1WICRpbl92Y2YgXAogICAgICAtLWtlZXAtaWRzICRzbnBzX2Rpci8kdHJhaXQubGlzdCBcCiAgICAgIC1PICRvdXRfZGlyLyR0cmFpdC52Y2YuZ3ogCiAgICAiIiIgOwpkb25lCmBgYAoKIyMgR2V0IGFsbGVsZSBmcmVxdWVuY2llcyBvZiBTTlAgaGl0cyB3aXRoIGBQbGluazJgCgojIyMgSW1wb3J0IDFLRyBtZXRhZGF0YSAoZm9yIHNhbXBsZS1wb3B1bGF0aW9uIGtleSkKCkRvd25sb2RlZCB2aWEgdGhpcyBwYWdlOiA8aHR0cDovL3d3dy5pbnRlcm5hdGlvbmFsZ2Vub21lLm9yZy9kYXRhPi4KCkRvd25sb2FkIGxpbms6IDxodHRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay92b2wxL2Z0cC90ZWNobmljYWwvd29ya2luZy8yMDEzMDYwNl9zYW1wbGVfaW5mby8yMDEzMDYwNl9zYW1wbGVfaW5mby54bHN4Pi4gCgpTYXZlZCBoZXJlOiBgZGF0YS8yMDEzMDYwNl9zYW1wbGVfaW5mby54bHN4YAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnYXNpcyd9CnNhbXBsZXNfZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxMzA2MDZfc2FtcGxlX2luZm8ueGxzeCIpCgptZXRhID0gcmVhZHhsOjpyZWFkX3hsc3goc2FtcGxlc19maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAiU2FtcGxlIEluZm8iKSAlPiUKICBkcGx5cjo6c2VsZWN0KFNhbXBsZSwgUG9wdWxhdGlvbiwgR2VuZGVyKQoKa25pdHI6OmthYmxlKGhlYWQobWV0YSkpCmBgYAoKIyMjIFdyaXRlIHBvcHVsYXRpb24gZmlsZSBmb3IgUGxpbmsyCgpgYGB7ciwgZXZhbCA9IEZ9CnNhbXBsZV9wb3BuX2tleV9maWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJwbGluazJfc2FtcGxlX3BvcG5fa2V5LnR4dCIpCgp3cml0ZS50YWJsZShtZXRhWywgMToyXSwKICAgICAgICAgICAgc2FtcGxlX3BvcG5fa2V5X2ZpbGUsCiAgICAgICAgICAgIHF1b3RlID0gRiwKICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRiwKICAgICAgICAgICAgY29sLm5hbWVzID0gRikKYGBgCgojIyMgUnVuIGBQbGluazJgIGZvciBTTlAgaGl0cwoKKFRha2Ugb25seSBiaWFsbGVsaWMgU05Qcy4pCgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbCA9IEZ9Cgpjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwKCiMgU2V0IHZhcmlhYmxlcwoKdHJhaXRzPSQoZWNobyBoZWkgYm1pIGVkdSBpbnQgaWJkIHBpZykKaW5fdmNmX2Rpcj1kYXRhLzIwMjEwMTI1X3NucF9oaXRzX2ZpbHRlcmVkCnBvcG5fZmlsZT1kYXRhL3BsaW5rMl9zYW1wbGVfcG9wbl9rZXkudHh0Cm91dF9kaXI9ZGF0YS8yMDIxMDEyMl9zbnBfaGl0c19hbGZyZXFzCgojIFNldCB1cCBkaXJlY3Rvcmllcwpta2RpciAtcCAkb3V0X2RpcgoKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzKTsgZG8KICBta2RpciAtcCAkb3V0X2Rpci8kdHJhaXQ7IApkb25lICAgCgojIFJ1biBQbGluazIKCiMjIEdldCBnbG9iYWwgQUYKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzKTsgZG8KICBwbGluazIgXAogICAgLS12Y2YgJGluX3ZjZl9kaXIvJHRyYWl0LnZjZi5neiBcCiAgICAtLWZyZXEgXAogICAgLS1tYXgtYWxsZWxlcyAyIFwKICAgIC0tc25wcy1vbmx5IFwKICAgIC0tb3V0ICRvdXRfZGlyLyR0cmFpdC8kdHJhaXQuYWxsCmRvbmUKCiMjIEdldCBBRiBwZXIgcG9wdWxhdGlvbgpmb3IgdHJhaXQgaW4gJChlY2hvICR0cmFpdHMpOyBkbwogIHBsaW5rMiBcCiAgICAtLXZjZiAkaW5fdmNmX2Rpci8kdHJhaXQudmNmLmd6IFwKICAgIC0tZnJlcSBcCiAgICAtLW1heC1hbGxlbGVzIDIgXAogICAgLS1zbnBzLW9ubHkgXAogICAgLS1waGVubyBpaWQtb25seSAkcG9wbl9maWxlIFwKICAgIC0tbG9vcC1jYXRzIFBIRU5PMSBcCiAgICAtLW91dCAkb3V0X2Rpci8kdHJhaXQvJHRyYWl0IDsKZG9uZQpgYGAKCiMjIyBBZGQgYWxsZWxlIGZyZXF1ZW5jeSBmaWxlcwoKYGBge3J9CnRhcmdldF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTIyX3NucF9oaXRzX2FsZnJlcXMiKQoKIyBHbG9iYWwKY291bnRlciA8LSAwCmRhdGFfbGlzdCA9IGxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBjb3VudGVyIAogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBnZXQgdHJhaXQgbmFtZQogIHRyYWl0ID0gbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXQogICMgZ2V0IGZpbGUgcGF0aAogIHRhcmdldF9wYXRoID0gZmlsZS5wYXRoKHRhcmdldF9kaXIsIHRyYWl0LCBwYXN0ZSh0cmFpdCwgIi5hbGwuYWZyZXEiLCBzZXAgPSAiIikpCiAgIyByZWFkIGluIGRhdGEKICBjbGVhbl9hZiA9IHJlYWRfYWZyZXEodGFyZ2V0X3BhdGgpCiAgIyBhZGQgUE9QTiBjb2x1bW4KICBjbGVhbl9hZiRQT1BOID0gImFsbCIKICAjIGFkZCB0byBsaXN0CiAgcGhlbm9bWyJjbGVhbl9hZiJdXSA9IGNsZWFuX2FmCiAgCiAgcmV0dXJuKHBoZW5vKQp9KQoKIyBQZXIgcG9wdWxhdGlvbgpjb3VudGVyIDwtIDAKZGF0YV9saXN0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IHRyYWl0IG5hbWUKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICAjIGdldCBmaWxlIHBhdGgKICB0YXJnZXRfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aCh0YXJnZXRfZGlyLCB0cmFpdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi4qW15hbGxdXFwuYWZyZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpCiAgIyBnZXQgcG9wbiBuYW1lcwogIG5hbWVzKHRhcmdldF9maWxlcykgPSBiYXNlbmFtZSh0YXJnZXRfZmlsZXMpICU+JSAKICAgIHN0cl9zcGxpdCgiXFwuIiwgc2ltcGxpZnkgPSBUKSAlPiUgCiAgICBzdWJzZXQoc2VsZWN0ID0gMikKICAjIHJlYWQgZmlsZXMgYW5kIGJpbmQgaW50byBzaW5nbGUgREYKICBwb3BuX2FmcmVxcyA9IGxhcHBseSh0YXJnZXRfZmlsZXMsIGZ1bmN0aW9uKHBvcG4pewogICAgZGYgPSByZWFkX2FmcmVxKHBvcG4pCiAgfSkgJT4lIAogICAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiUE9QTiIpIyAlPiUgCiMgICAgZHBseXI6OnNlbGVjdCgtT0JTX0NUKSAlPiUgCiMgICAgdGlkeXI6OnBpdm90X3dpZGVyKGlkX2NvbHMgPSBTTlAsIG5hbWVzX2Zyb20gPSBQT1BOLCB2YWx1ZXNfZnJvbSA9IEFMVF9GUkVRUykKICAjIGNvbWJpbmUgd2l0aCBgY2xlYW5fYWZgCiAgcGhlbm9bWyJjbGVhbl9hZiJdXSA9IGRwbHlyOjpiaW5kX3Jvd3MocGhlbm9bWyJjbGVhbl9hZiJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3BuX2FmcmVxcykKICAKICByZXR1cm4ocGhlbm8pCn0pCmBgYAoKIyMgU2V0IHVwIG5lZ2F0aXZlIGNvbnRyb2xzCgpQdWxsIG91dCByYW5kb20gU05QcyB3aXRoIHRoZSBzYW1lIGdsb2JhbCBhbGxlbGUgZnJlcXVlbmNpZXMgYXMgdGhlIEdXQVMgU05QLWhpdHMKCiMjIyBCaW4gU05QIGhpdHMgYnkgYWxsZWxlIGZyZXF1ZW5jeQoKQmluZCB0byBjbGVhbiBERiB0byBnZXQgQUZzIG9mIHJpc2sgYWxsZWxlCgpbKipOT1RFKipde2NvbG9yPSJyZWQifTogSWYgYFJJU0tfQUxMRUxFYCBpcyB1bmtub3duLCBzZXQgdGhlIGFsbGVsZSBmcmVxdWVuY3kgdG8gYEFMVF9GUkVRU2AKCmBgYHtyfQpkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBqb2luIERGcwogIGRmID0gZHBseXI6OmxlZnRfam9pbihwaGVub1tbImNsZWFuIl1dLAogICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KHBoZW5vW1siY2xlYW5fYWYiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLUNIUiksCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiVE9QX1NOUCIgPSAiU05QIikpCiAgIyBnZXQgQUYgb2YgcmlzayBhbGxlbGUKICBkZiRSSVNLX0FGID0gZHBseXI6OmlmX2Vsc2UoZGYkUklTS19BTExFTEUgPT0gIj8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZiRBTFRfRlJFUVMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjppZl9lbHNlKGRmJFJJU0tfQUxMRUxFID09IGRmJEFMVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGYkQUxUX0ZSRVFTLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxIC0gZGYkQUxUX0ZSRVFTKSkKICAjIGFkZCBgSElUX0NPTlRST0xgIGNvbHVtbgogIGRmJEhJVF9DT05UUk9MID0gImhpdCIKICAjIGFkZCB0byBsaXN0CiAgcGhlbm9bWyJjb25zb2wiXV0gPSBkZgogIAogIHJldHVybihwaGVubykKfSkKYGBgCgoKQmluIGJ5IHJpc2sgYWxsZWxlIGZyZXF1ZW5jeQoKYGBge3J9CiMgMSUgaW50ZXJ2YWxzCmJyZWFrcG9pbnRzID0gc2VxKDAsIDEsIDAuMSkKCmRhdGFfbGlzdCA9IGxhcHBseShkYXRhX2xpc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIGNob29zZSBERgogIGRmID0gcGhlbm9bWyJjb25zb2wiXV0KICAjIGFkZCBiaW5zCiAgZGYkQklOXzEwID0gY3V0KGRmJFJJU0tfQUYsIGJyZWFrcyA9IGJyZWFrcG9pbnRzLCBsYWJlbHMgPSBGKQogICMgc2F2ZSBiYWNrIGludG8gbGlzdAogIHBoZW5vW1siY29uc29sIl1dID0gZGYKICAKICByZXR1cm4ocGhlbm8pCn0pCgpgYGAKCkV4dHJhY3Qga2V5IGNvbHVtbnMgYW5kIHdyaXRlIHRvIGZpbGUKCmBgYHtyLCBldmFsID0gRiwgbWVzc2FnZT1GfQpvdXRfZGlyID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDIwMV9zbnBfcmlza19oaXRzX2Jpbm5lZCIpCgpkaXIuY3JlYXRlKG91dF9kaXIpCgojIFNhdmUgbGlzdApjb3VudGVyIDwtIDAKcmlza19hZnMgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBzZXQgY291bnRlciAKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IHRhcmdldCBERgogIGRmID0gcGhlbm9bWyJjb25zb2wiXV0KICAjIGZpbHRlcgogIGRmID0gZGYgJT4lIAogICAgZHBseXI6OmZpbHRlcihQT1BOID09ICJhbGwiKSAlPiUgIyB0YWtlIG9ubHkgZ2xvYmFsIEFGcyAKICAgIGRwbHlyOjpzZWxlY3QoVE9QX1NOUCwgQklOXzEwKSAlPiUgCiAgICB0aWR5cjo6ZHJvcF9uYSgpICMgZHJvcCBOQXMKICAjIHNldCBvdXRwdXQgcGF0aAogIHRyYWl0ID0gbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXQogIHBhdGhfb3V0ID0gZmlsZS5wYXRoKG91dF9kaXIsIHBhc3RlKHRyYWl0LCAiLnR4dCIsIHNlcCA9ICIiKSkKICAjIHdyaXRlIHRvIGZpbGUKICByZWFkcjo6d3JpdGVfdHN2KGRmLCBwYXRoX291dCkKfSkKCgpgYGAKCiMjIyBCaW4gMUtHIFNOUHMgCgojIyMjIEdldCBhbGxlbGUgZnJlcXVlbmNpZXMgZnJvbSAxS0cKCldpdGggYFBsaW5rMmAsIHBlciBjaHJvbW9zb21lIGZvciBzcGVlZC4KCmBgYHtyLCBlbmdpbmU9J2Jhc2gnLCBldmFsPUZ9CiMgc2V0IG91dHB1dCBkaXJlY3RvcnkKaW5fZmlsZT0uLi92Y2ZzLzFrZ19hbGwudmNmLmd6Cm91dF9kaXI9Li4vYmlnX2RhdGEvMjAyMTAxMjVfYWxmcmVxc19hbGwKCm1rZGlyIC1wICRvdXRfZGlyCgojIFBlciBjaHJvbW9zb21lCmZvciBjaHIgaW4gJChzZXEgMSAyMikgOyBkbwogICMgY3JlYXRlIGFsbGVsZS1mcmVxIHRhYmxlcwogIGJzdWIgXAogICAgLU0gMTAwMDAgXAogICAgLW8gLi4vbG9nLzIwMjEwMTI1X3BsaW5rX2FsZnJlcV8kY2hyLm91dCBcCiAgICAtZSAuLi9sb2cvMjAyMTAxMjVfcGxpbmtfYWxmcmVxXyRjaHIuZXJyIFwKICAgICIiIgogICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgIHBsaW5rMiBcCiAgICAgIC0tdmNmICRpbl9maWxlIFwKICAgICAgLS1mcmVxIFwKICAgICAgLS1jaHIgJGNociBcCiAgICAgIC0tbWF4LWFsbGVsZXMgMiBcCiAgICAgIC0tc25wcy1vbmx5IFwKICAgICAgLS1vdXQgJG91dF9kaXIvJGNociAiOwpkb25lIApgYGAKCiMjIyMgQmluIHRoZW0gYW5kIHNhdmUgdG8gc2luZ2xlIGZpbGUKCmBgYHtyLCBldmFsID0gRn0KIyBPbiBjbHVzdGVyCiMgYGNvbmRhIGFjdGl2YXRlIGZzdF9lbnZfcmhlbGAKCmxpYnJhcnkoaGVyZSkKc291cmNlKGhlcmU6OmhlcmUoImNvZGUiLCAic2NyaXB0cyIsICJzb3VyY2UuUiIpKQoKIyBTZXQgdmFyaWFibGVzCmluX2RpciA9ICIuLi9iaWdfZGF0YS8yMDIxMDEyNV9hbGZyZXFzX2FsbCIKb3V0X2RpciA9ICIuLi9iaWdfZGF0YS8yMDIxMDIwMV9hbGZyZXFzX2FsbF9iaW5uZWRfMTAiCmJyZWFrcG9pbnRzID0gc2VxKDAsIDEsIDAuMSkgIyAxJSBiaW5zCgojIENyZWF0ZSBvdXRwdXQgZGlyZWN0b3J5CmRpci5jcmVhdGUob3V0X2RpcikKCiMgR2V0IGxpc3Qgb2YgaW5wdXQgZmlsZXMKaW5fZmlsZXMgPSBsaXN0LmZpbGVzKGluX2RpciwgcGF0dGVybiA9ICIuYWZyZXEiLCBmdWxsLm5hbWVzID0gVCkKCiMgUmVhZCBpbiBmaWxlcywgYWRkIGJpbnMsIGFuZCB3cml0ZSB0byBvdXRwdXQKZnJlcV9saXN0ID0gbGFwcGx5KGluX2ZpbGVzLCBmdW5jdGlvbihjaHJfZmlsZSl7CiAgIyByZWFkIGluIGZpbGUKICBkZiA9IHJlYWRfYWZyZXEoY2hyX2ZpbGUpCiAgIyBhZGQgYmlucwogIGRmJEJJTl8xMCA9IGN1dChkZiRBTFRfRlJFUVMsIGJyZWFrcyA9IGJyZWFrcG9pbnRzLCBsYWJlbHMgPSBGKQogICMgd3JpdGUgZmlsZQogIHJlYWRyOjp3cml0ZV90c3YoZGYsIGZpbGUgPSBmaWxlLnBhdGgob3V0X2RpciwgYmFzZW5hbWUoY2hyX2ZpbGUpKSkKfSkKCiMgQ29tYmluZSBpbnRvIHNpbmdsZSBERgpmcmVxX2RmID0gZHBseXI6OmJpbmRfcm93cyhmcmVxX2xpc3QpCgojIFdyaXRlIHRvIGZpbGUKcmVhZHI6OndyaXRlX3RzdihmcmVxX2RmLCBmaWxlID0gZmlsZS5wYXRoKG91dF9kaXIsICJhbGwuYWZyZXEiKSkKYGBgCgojIyMjIFB1bGwgb3V0IHJhbmRvbSBTTlBzIHdpdGggc2FtZSBBRiBhcyB0cmFpdCByaXNrIGFsbGVsZXMKCmBgYHtyLCBldmFsID0gRn0KIyBPbiBjbHVzdGVyCgpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlOjpoZXJlKCJjb2RlIiwgInNjcmlwdHMiLCAic291cmNlLlIiKSkKCiMgVmFyaWFibGVzCgppbnB1dF9yaXNrX3NucF9kaXIgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMjAxX3NucF9yaXNrX2hpdHNfYmlubmVkIikKYWxsXzFrZ19iaW5zID0gIi4uL2JpZ19kYXRhLzIwMjEwMjAxX2FsZnJlcXNfYWxsX2Jpbm5lZC9hbGwuYWZyZXEiCmluaXRpYWxfc2VlZCA9IDEyMwpvdXRwdXRfZGlyID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDIwMV9yYW5kb21fc25wcyIpCm91dHB1dF9kaXJfc25waWRzID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDIwMV9yYW5kb21fc25wc19zbnBfaWRzIikKCmRpci5jcmVhdGUob3V0cHV0X2RpcikKZGlyLmNyZWF0ZShvdXRwdXRfZGlyX3NucGlkcykKCiMjIFJlYWQgaW4gdGFyZ2V0IFNOUCBERiBhbmQgc3BsaXQgaW50byBsaXN0IGJ5IGJpbgpwaGVub3MgPSBnc3ViKCIudHh0IiwgIiIsIGJhc2VuYW1lKGxpc3QuZmlsZXMoaW5wdXRfcmlza19zbnBfZGlyKSkpCm5hbWVzKHBoZW5vcykgPSBwaGVub3MKCnJpc2tfbGlzdCA9IGxhcHBseShwaGVub3MsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBmaWxlIHBhdGgKICBmaWxlX3BhdGggPSBmaWxlLnBhdGgoaW5wdXRfcmlza19zbnBfZGlyLCBwYXN0ZShwaGVubywgIi50eHQiLCBzZXAgPSAiIikpCiAgIyByZWFkIGZpbGUKICBvdXQgPSByZWFkcjo6cmVhZF90c3YoZmlsZV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBUKSAlPiUgCiAgICBzcGxpdCguLCBmID0gLiRCSU5fMTAwKSAjIHNwbGl0IGJ5IGJpbgp9KQogIAojIyBSZWFkIGluIDFLRyBkYXRhCmZyZXFfZGYgPSByZWFkcjo6cmVhZF90c3YoYWxsXzFrZ19iaW5zKQoKIyBGb3IgZWFjaCBiaW4gaW4gYHJpc2tfbGlzdGAsIHB1bGwgb3V0IHRoZSBzYW1lIG51bWJlciBvZiByYW5kb20gbnVtYmVyIDFLRyBTTlBzIHdpdGggdGhlIHNhbWUgYmluCgojIyBTZXQgc2VlZApzZXQuc2VlZChpbml0aWFsX3NlZWQpCgojIyBHZXQgc2VlZHMgZm9yIGVhY2ggYmluCnNlZWRzID0gc2FtcGxlKDE6MTAwMCwgbGVuZ3RoKHJpc2tfbGlzdCkpCgojIyBSdW4gb3ZlciBsaXN0CmNvdW50ZXIgPC0gMApsYXBwbHkocmlza19saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBzZXQgY291bnRlciAKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMSAgCiAgIyBzZXQgc2VlZCBmb3IgcGhlbm8KICBzZXQuc2VlZChzZWVkcylbY291bnRlcl0KICAjIGdldCBzZWVkcyBmb3IgYmluCiAgYmluX3NlZWRzID0gc2FtcGxlKDE6MTAwMCwgbGVuZ3RoKHBoZW5vKSkKICAjIGdldCByYW5kb20gU05QcyBmcm9tIGVhY2ggYmluCiAgYmluX2NvdW50ZXIgPC0gMAogIG91dCA9IGxhcHBseShwaGVubywgZnVuY3Rpb24oYmluX2RmKXsKICAgICMgc2V0IGBiaW5fY291bnRlcmAKICAgIGJpbl9jb3VudGVyIDw8LSBiaW5fY291bnRlciArIDEKICAgICMgZ2V0IHRhcmdldCBiaW4KICAgIHRhcmdldF9iaW4gPSBhcy5pbnRlZ2VyKG5hbWVzKHBoZW5vKVtiaW5fY291bnRlcl0pCiAgICAjIGdldCBudW1iZXIgb2YgbWF0Y2hlcyByZXF1aXJlZAogICAgaGl0c19uID0gbnJvdyhiaW5fZGYpCiAgICAjIHNldCBzZWVkCiAgICBzZXQuc2VlZChiaW5fc2VlZHNbYmluX2NvdW50ZXJdKSAgICAKICAgICMgZmlsdGVyIDFrZyBERiBmb3IgU05QcyB3aXRoIHNhbWUgYmluIGFuZCBnZXQgcmFuZG9tIGhpdHMKICAgIHJhbmRvbV9oaXRzID0gZnJlcV9kZiAlPiUgCiAgICAgICNkcGx5cjo6c2VsZWN0KFNOUCwgQUxUX0ZSRVFTLCBPQlNfQ1QsIEJJTl8xMDApICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihCSU5fMTAwID09IHRhcmdldF9iaW4pICU+JSAKICAgICAgZHBseXI6OnNsaWNlX3NhbXBsZShuID0gaGl0c19uKSAlPiUgCiAgICAgIGRwbHlyOjpyZW5hbWUoUkFORE9NX1NOUCA9IFNOUCwKICAgICAgICAgICAgICAgICAgICBSQU5ET01fQklOXzEwMCA9IEJJTl8xMDApCiAgICAjIGJpbmQgYHJhbmRvbV9oaXRzYCB0byB0YXJnZXQgU05QIGRmCiAgICBkZl9vdXQgPSBjYmluZChiaW5fZGYsIHJhbmRvbV9oaXRzKQogICAgCiAgICByZXR1cm4oZGZfb3V0KQogIH0pICU+JSAKICAgICMgYmluZCBpbnRvIHNpbmdsZSBkYXRhIGZyYW1lCiAgICBkcGx5cjo6YmluZF9yb3dzKCkKICAKICAjIHNhdmUgdG8gZmlsZQogICMjIHNldCBvdXRwdXQgcGF0aAogIHRyYWl0ID0gbmFtZXMocmlza19saXN0KVtjb3VudGVyXQogIG91dF9wYXRoID0gZmlsZS5wYXRoKG91dHB1dF9kaXIsIHBhc3RlKHRyYWl0LCAiLnR4dCIsIHNlcCA9ICIiKSkKICAjIyB3cml0ZSBmaWxlCiAgcmVhZHI6OndyaXRlX3RzdihvdXQsIG91dF9wYXRoKQogIAogICMgc2F2ZSBqdXN0IFNOUCBJRHMgKGZvciBQbGluayB0byBnZXQgcGVyLXBvcHVsYXRpb24gQUZzKQogIG91dF9wYXRoID0gZmlsZS5wYXRoKG91dHB1dF9kaXJfc25waWRzLCBwYXN0ZSh0cmFpdCwgIi5saXN0Iiwgc2VwID0gIiIpKQogICMjIHdyaXRlIGZpbGUKICByZWFkcjo6d3JpdGVfbGluZXMob3V0JFJBTkRPTV9TTlAsIG91dF9wYXRoKSAgCn0pCgpgYGAKCiMjIyMgRmlsdGVyIFZDRnMgZm9yIHJhbmRvbSBTTlBzCgpgYGB7YmFzaCwgZXZhbCA9IEZ9CnRyYWl0cz0kKGVjaG8gaGVpIGJtaSBlZHUgaW50IGliZCBwaWcpCnJlZj0uLi9yZWZzL2hzMzdkNS5mYS5negppbl92Y2Y9Li4vdmNmcy8xa2dfYWxsLnZjZi5negpzbnBzX2Rpcj1kYXRhLzIwMjEwMTI2X3JhbmRvbV9zbnBzX3NucF9pZHMKb3V0X2Rpcj1kYXRhLzIwMjEwMTI3X3NucF9ybmRtX2ZpbHRlcmVkCgpta2RpciAtcCAkb3V0X2RpcgoKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCiAgYnN1YiBcCiAgICAtTSAxMDAwMCBcCiAgICAtbyAuLi9sb2cvMjAyMTAxMjdfZXh0cmFjdF9zbnBzXyR0cmFpdC5vdXQgXAogICAgLWUgLi4vbG9nLzIwMjEwMTI3X2V4dHJhY3Rfc25wc18kdHJhaXQuZXJyIFwKICAgICIiIgogICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgIGdhdGsgU2VsZWN0VmFyaWFudHMgXAogICAgICAtUiAkcmVmIFwKICAgICAgLVYgJGluX3ZjZiBcCiAgICAgIC0ta2VlcC1pZHMgJHNucHNfZGlyLyR0cmFpdC5saXN0IFwKICAgICAgLU8gJG91dF9kaXIvJHRyYWl0LnZjZi5neiAKICAgICIiIiA7CmRvbmUKYGBgCgojIyMjIEdldCBwZXItcG9wdWxhdGlvbiBhbGxlbGUgZnJlcXVlbmNpZXMgb2YgcmFuZG9tIFNOUHMgCgpgYGB7YmFzaCwgZXZhbCA9IEZ9CnRyYWl0cz0kKGVjaG8gaGVpIGJtaSBlZHUgaW50IGliZCBwaWcpCnJhbmRvbV9zbnBzX2Rpcj1kYXRhLzIwMjEwMTI2X3JhbmRvbV9zbnBzX3NucF9pZHMKdmNmX2luX2Rpcj1kYXRhLzIwMjEwMTI3X3NucF9ybmRtX2ZpbHRlcmVkCnBvcG5fa2V5PWRhdGEvcGxpbmsyX3NhbXBsZV9wb3BuX2tleS50eHQKb3V0X2Rpcj1kYXRhLzIwMjEwMTI3X3NucF9ybmRtX2FsZnJlcXMKCgpmb3IgdHJhaXQgaW4gJChlY2hvICR0cmFpdHMgKTsgZG8KCiAgbWtkaXIgLXAgJG91dF9kaXIvJHRyYWl0CiAgCiAgYnN1YiBcCiAgICAtTSAxMDAwMCBcCiAgICAtbyAuLi9sb2cvMjAyMTAxMjdfcmRtX3BvcG5fYWZyZXFzXyR0cmFpdC5vdXQgXAogICAgLWUgLi4vbG9nLzIwMjEwMTI3X3JkbV9wb3BuX2FmcmVxc18kdHJhaXQuZXJyIFwKICAgICIiIgogICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgIHBsaW5rMiBcCiAgICAgIC0tdmNmICR2Y2ZfaW5fZGlyLyR0cmFpdC52Y2YuZ3ogXAogICAgICAtLWV4dHJhY3QgJHJhbmRvbV9zbnBzX2Rpci8kdHJhaXQubGlzdCBcCiAgICAgIC0tZnJlcSBcCiAgICAgIC0tcGhlbm8gaWlkLW9ubHkgJHBvcG5fa2V5IFwKICAgICAgLS1sb29wLWNhdHMgUEhFTk8xIFwKICAgICAgLS1vdXQgJG91dF9kaXIvJHRyYWl0LyR0cmFpdAogICAgIiIiIDsKZG9uZSAgCmBgYAoKIyMjIyBCaW5kIGludG8gc2luZ2xlIERGCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmluX2Rpcl9ybmRtID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyNl9yYW5kb21fc25wcyIpCmluX2Rpcl9hZnJlcSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjdfc25wX3JuZG1fYWxmcmVxcyIpCgojIFJlYWQgaW4gZGF0YQoKIyMgUmFuZG9tIFNOUHMKaW5fZmlsZXNfcm5kbSA9IGxpc3QuZmlsZXMoaW5fZGlyX3JuZG0sIGZ1bGwubmFtZXMgPSBUKQpuYW1lcyhpbl9maWxlc19ybmRtKSA9IGdzdWIoIi50eHQiLCAiIiwgYmFzZW5hbWUoaW5fZmlsZXNfcm5kbSkpCgpyYW5kb21fc25wcyA9IGxhcHBseShpbl9maWxlc19ybmRtLCBmdW5jdGlvbihmaWxlKXsKICBvdXQgPSByZWFkcjo6cmVhZF90c3YoZmlsZSkKICAjIGFkZCBgUE9QTmAgY29sdW1uCiAgb3V0JFBPUE4gPSAiYWxsIgogIAogIHJldHVybihvdXQpCn0pICU+JSAKICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJQSEVOTyIpCgojIyBQb3BuIGFmcmVxcwpwb3BuX2FmcmVxcyA9IGxhcHBseSh0cmFpdF9sZXZlbHMsIGZ1bmN0aW9uKHBoZW5vKXsKICB0YXJnZXRfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aChpbl9kaXJfYWZyZXEsIHBoZW5vKSwgcGF0dGVybiA9ICIuYWZyZXEiLCBmdWxsLm5hbWVzID0gVCkKICAKICBuYW1lcyh0YXJnZXRfZmlsZXMpID0gYmFzZW5hbWUodGFyZ2V0X2ZpbGVzKSAlPiUgCiAgICBzdHJfc3BsaXQoIlxcLiIsIHNpbXBsaWZ5ID0gVCkgJT4lIAogICAgc3Vic2V0KHNlbGVjdCA9IDIpCiAgCiAgcG9wbl9hZnJlcXMgPSBsYXBwbHkodGFyZ2V0X2ZpbGVzLCBmdW5jdGlvbihwb3BuKXsKICAgIGRmID0gcmVhZF9hZnJlcShwb3BuKQogIH0pICU+JSAKICAgIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gIlBPUE4iKSAjICU+JSAKICAjICBkcGx5cjo6c2VsZWN0KC1PQlNfQ1QpICU+JSAKICAjICB0aWR5cjo6cGl2b3Rfd2lkZXIoaWRfY29scyA9IFNOUCwgbmFtZXNfZnJvbSA9IFBPUE4sIHZhbHVlc19mcm9tID0gYyhBTFRfRlJFUVMsIE9CU19DVCkpCiAgCiAgcmV0dXJuKHBvcG5fYWZyZXFzKQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiUEhFTk8iKQoKIyBCaW5kIGBwb3BuX2FmcmVxc2AgdG8gYHJhbmRvbV9zbnBzYApyYW5kb21fYWZyZXFzID0gZHBseXI6OmZ1bGxfam9pbihkcGx5cjo6c2VsZWN0KHJhbmRvbV9zbnBzLCAtYyhQT1BOLCBBTFRfRlJFUVMsIE9CU19DVCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3BuX2FmcmVxcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJQSEVOTyIsICJSQU5ET01fU05QIiA9ICJTTlAiLCAiUkVGIiwgIkFMVCIsICJDSFIiKSkKIyBCaW5kIGBhbGxgIEFGcwpyYW5kb21fYWZyZXFzID0gcmJpbmQocmFuZG9tX2FmcmVxcywgcmFuZG9tX3NucHMpCgojIyBSZWNvZGUgUEhFTk8KI3JhbmRvbV9hZnJlcXMkUEhFTk8gPSBkcGx5cjo6cmVjb2RlKHJhbmRvbV9hZnJlcXMkUEhFTk8sICEhIXJldl9yZWNvZGVfdmVjKQoKIyMgQWRkIGBISVRfQ09OVFJPTGAgY29sdW1uCnJhbmRvbV9hZnJlcXMkSElUX0NPTlRST0wgPSAiY29udHJvbCIKYGBgCgojIyMjIEFkZCB0byBgZGF0YV9saXN0YAoKYGBge3J9CmNvdW50ZXIgPC0gMApkYXRhX2xpc3QgPSBsYXBwbHkoZGF0YV9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgIyBzZXQgY291bnRlcgogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBzZXQgdGFyZ2V0IHBoZW5vCiAgdGFyZ2V0X3BoZW5vID0gbmFtZXMoZGF0YV9saXN0KVtjb3VudGVyXQogICMgYWRkIHJhbmRvbSBhZnJlcXMKICByYW5kb21fYWYgPSByYW5kb21fYWZyZXFzICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoUEhFTk8gPT0gdGFyZ2V0X3BoZW5vKQogICMgcmVjb2RlIFBIRU5PCiAgcmFuZG9tX2FmJFBIRU5PID0gZHBseXI6OnJlY29kZShyYW5kb21fYWYkUEhFTk8sICEhIXJlY29kZV92ZWMpCiAgIyBhZGQgdG8gbGlzdAogIHBoZW5vW1sicmFuZG9tX2FmIl1dID0gcmFuZG9tX2FmCiAgCiAgcmV0dXJuKHBoZW5vKQp9KQpgYGAKCiMjIENsdW1wIHRvIGdldCBsZWFkIFNOUHMKClVzZSBgUGxpbmsxLjlgIChgUGxpbmsyLjBgIGRvZXNuJ3QgaGF2ZSBhIGBjbHVtcGAgZnVuY3Rpb24uKQoKRnJvbSB0aGUgYFBsaW5rMS43YCBkb2N1bWVudGF0aW9uICg8aHR0cDovL3p6ei5id2guaGFydmFyZC5lZHUvcGxpbmsvY2x1bXAuc2h0bWw+KSwgd2hpY2ggYXBwbGllcyB0byBgUGxpbmsxLjlgOgoKPiBUaGUgY2x1bXBpbmcgcHJvY2VkdXJlIHRha2VzIGFsbCBTTlBzIHRoYXQgYXJlIHNpZ25pZmljYW50IGF0IHRocmVzaG9sZCBwMSB0aGF0IGhhdmUgbm90IGFscmVhZHkgYmVlbiBjbHVtcGVkIChkZW5vdGluZyB0aGVzZSBhcyBpbmRleCBTTlBzKSBhbmQgZm9ybXMgY2x1bXBzIG9mIGFsbCBvdGhlciBTTlBzIHRoYXQgYXJlIHdpdGhpbiBhIGNlcnRhaW4ga2IgZGlzdGFuY2UgZnJvbSB0aGUgaW5kZXggU05QIChkZWZhdWx0IDI1MGtiKSBhbmQgdGhhdCBhcmUgaW4gbGlua2FnZSBkaXNlcXVpbGlicml1bSB3aXRoIHRoZSBpbmRleCBTTlAsIGJhc2VkIG9uIGFuIHItc3F1YXJlZCB0aHJlc2hvbGQgKGRlZmF1bHQgMC41MCkuLi4gVGhpcyBpcyBhIGdyZWVkeSBhbGdvcml0aG0gYW5kIHNvIGVhY2ggU05QIHdpbGwgb25seSBhcHBlYXIgaW4gYSBzaW5nbGUgY2x1bXAsIGlmIGF0IGFsbC4gCgo+IC4uLlt0XWhlIFRPVEFMIGZpZWxkIGxpc3RzIGFsbCBTTlBzIHRoYXQgYXJlIGNsdW1wZWQgd2l0aCB0aGUgaW5kZXggU05QLCBpcnJlc3BlY3RpdmUgb2YgdGhlIHAtdmFsdWUgZm9yIHRob3NlIFNOUHMuIFRoaXMgbnVtYmVyIGlzIHRoZW4gc3BsaXQgaW50byB0aG9zZSBjbHVtcGVkIFNOUHMgdGhhdCBhcmUgbm90IHNpZ25pZmljYW50IChwPjAuMDUpIGFuZCB2YXJpb3VzIG90aGVyIGdyb3VwcyBkZWZpbmVkIGJ5IHNpZ25pZmljYW5jZSB0aHJlc2hvbGRzLiBGb3IgU05QcyB0aGF0IGFyZSBzaWduaWZpY2FudCBhdCB0aGUgcDIgdGhyZXNob2xkLCB0aGV5IGFyZSBsaXN0ZWQgZXhwbGljaXRseS4gVGhlICgxKSBhZnRlciBlYWNoIFNOUCBuYW1lIHJlZmVycyB0byB0aGUgcmVzdWx0cyBmaWxlIHRoZXkgY2FtZSBmcm9tIChpbiB0aGlzIGNhc2UsIHRoZXJlIGlzIG9ubHkgYSBzaW5nbGUgcmVzdWx0IGZpbGUgc3BlY2lmaWVkLCBzbyBhbGwgdmFsdWVzIGFyZSAxKS4KCkhlcmUsIHdlJ3JlIHRha2luZyBhbGwgU05QcyB3aXRoICpQKiA8IDFlLTA4IGFzIGluZGV4IFNOUHMsIGFuZCBpdCB3aWxsIGV4cGxpY2l0bHkgbGlzdCBhbGwgU05QcyB3aXRoaW4gdGhlIGNsdW1wIHRoYXQgYWxzbyBtZWV0IHRoYXQgdGhyZXNob2xkLiAKCmBgYHtiYXNoLCBldmFsID0gRn0KCiMgQWN0aXZhdGUgZW52aXJvbm1lbnQKY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsCgojIFNldCB2YXJpYWJsZXMKdHJhaXRzPSQoZWNobyBoZWkgYm1pIGVkdSBpbnQgaWJkIHBpZykKaW5fdmNmX2Rpcj1kYXRhLzIwMjEwMTI1X3NucF9oaXRzX2ZpbHRlcmVkCnNucF9wX2Rpcj1kYXRhLzIwMjEwMTIyX3NucF9oaXRfbGlzdHMKb3V0X2Rpcj1kYXRhLzIwMjEwMTI1X2NsdW1wZWQKCnIyX3BhcmFtcz0kKGVjaG8gMC4xIDAuMiAwLjMpCmtiX3BhcmFtcz0kKGVjaG8gNTAwIDc1MCAxMDAwICkKCiMgTWFrZSBkaXJlY3RvcnkKbWtkaXIgLXAgJG91dF9kaXIKCiMgUnVuIHdpdGggZGlmZmVyZW50IHBhcmFtZXRlcnMKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCgogIG1rZGlyIC1wICRvdXRfZGlyLyR0cmFpdCA7CiAgCiAgZm9yIHIyIGluICRyMl9wYXJhbXMgOyBkbwogICAgZm9yIGtiIGluICRrYl9wYXJhbXMgOyBkbwogICAgICBic3ViIFwKICAgICAgICAtbyAuLi9sb2cvMjAyMTAxMjVfY2x1bXBfJHRyYWl0XF8kcjJcXyRrYi5vdXQgXAogICAgICAgIC1lIC4uL2xvZy8yMDIxMDEyNV9jbHVtcF8kdHJhaXRcXyRyMlxfJGtiLmVyciBcCiAgICAgICAgIiIiCiAgICAgICAgY29uZGEgYWN0aXZhdGUgZnN0X2Vudl9yaGVsIDsKICAgICAgICBwbGluayBcCiAgICAgICAgICAtLXZjZiAkaW5fdmNmX2Rpci8kdHJhaXQudmNmLmd6IFwKICAgICAgICAgIC0tY2x1bXAgJHNucF9wX2Rpci8kdHJhaXRcX3dpdGhfUC50eHQgXAogICAgICAgICAgLS1jbHVtcC1wMSAwLjAwMDAwMDAxIFwKICAgICAgICAgIC0tY2x1bXAtcDIgMC4wMDAwMDAwMSBcCiAgICAgICAgICAtLWNsdW1wLXIyICRyMiBcCiAgICAgICAgICAtLWNsdW1wLWtiICRrYiBcCiAgICAgICAgICAtLW91dCAkb3V0X2Rpci8kdHJhaXQvcjItJHIyXF9rYi0ka2IgCiAgICAgICAgIiIiICA7CiAgICBkb25lIDsKICBkb25lOyAgCmRvbmUKCmBgYAoKIyMgRmlsdGVyIFZDRnMgZm9yIGNsdW1wZWQgU05QcwoKYGBge3IsIGVuZ2luZSA9ICdiYXNoJywgZXZhbCA9IEZ9CiMgT24gY2x1c3RlcgoKIyBTZXQgdmFyaWFibGVzCnRyYWl0cz0kKGVjaG8gaGVpIGJtaSBlZHUgaW50IGliZCBwaWcpCmNsdW1wX3BhcmFtPSJyMi0wLjFfa2ItMTAwMCIKaW5fZGlyX3NucD1kYXRhLzIwMjEwMTI1X2NsdW1wZWQKaW5fZGlyX3ZjZj1kYXRhLzIwMjEwMTI1X3NucF9oaXRzX2ZpbHRlcmVkCnJlZj0uLi9yZWZzL2hzMzdkNS5mYS5negpvdXRfZGlyX3NucF9saXN0PWRhdGEvMjAyMTAxMjhfY2x1bXBlZF9zbnBzCm91dF9kaXJfdmNmcz1kYXRhLzIwMjEwMTI4X2hpdHNfdmNmcwoKbWtkaXIgLXAgJG91dF9kaXJfc25wX2xpc3QgJG91dF9kaXJfdmNmcwoKIyBFeHRyYWN0IFNOUCBjb2x1bW4gYW5kIHdyaXRlIHRvIGxpc3QKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCiAgdGFyZ2V0X2ZpbGU9JGluX2Rpcl9zbnAvJHRyYWl0LyRjbHVtcF9wYXJhbS5jbHVtcGVkIDsKICBhd2sgJ3twcmludCAkM30nICR0YXJnZXRfZmlsZSB8IHRhaWwgLW4rMiBcCiAgICA+ICRvdXRfZGlyX3NucF9saXN0LyR0cmFpdC5saXN0IDsKZG9uZQoKIyBNYWtlIG5ldyBWQ0ZzCmZvciB0cmFpdCBpbiAkKGVjaG8gJHRyYWl0cyApOyBkbwogIGJzdWIgXAogICAgLW8gLi4vbG9nLzIwMjEwMTI4X2ZpbHRlcl92Y2ZzX2NsdW1wXyR0cmFpdC5vdXQgXAogICAgLWUgLi4vbG9nLzIwMjEwMTI4X2ZpbHRlcl92Y2ZzX2NsdW1wXyR0cmFpdC5lcnIgXAogICAgIiIiCiAgICBjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwgOwogICAgZ2F0ayBTZWxlY3RWYXJpYW50cyBcCiAgICAgIC1SICRyZWYgXAogICAgICAtViAkaW5fZGlyX3ZjZi8kdHJhaXQudmNmLmd6IFwKICAgICAgLS1rZWVwLWlkcyAkb3V0X2Rpcl9zbnBfbGlzdC8kdHJhaXQubGlzdCBcCiAgICAgIC1PICRvdXRfZGlyX3ZjZnMvJHRyYWl0LnZjZi5neiAgICAgCiAgICAiIiIgOwpkb25lICAgIAoKYGBgCgojIyMgQWRkIGNsdW1wZWQgU05QIGZpbGVzIHRvIGBkYXRhX2xpc3RgCgpgYGB7cn0KdGFyZ2V0X2RpciA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjVfY2x1bXBlZCIpCgpjb3VudGVyIDwtIDAKZGF0YV9saXN0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IHRyYWl0IG5hbWUKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICAjIGdldCBmaWxlIHBhdGgKICB0YXJnZXRfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aCh0YXJnZXRfZGlyLCB0cmFpdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi5jbHVtcGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKQogIG5hbWVzKHRhcmdldF9maWxlcykgPSBnc3ViKCIuY2x1bXBlZCIsICIiLCBiYXNlbmFtZSh0YXJnZXRfZmlsZXMpKQogICMgcmVhZCBmaWxlcyBhcyBgY2x1bXBlZGAKICBjb3VudGVyX2NsdW1wIDwtIDAKICBjbHVtcGVkID0gbGFwcGx5KHRhcmdldF9maWxlcywgZnVuY3Rpb24ocGFyYW1zKXsKICAgICMgc2V0IGNvdW50ZXIKICAgIGNvdW50ZXJfY2x1bXAgPDwtIGNvdW50ZXJfY2x1bXAgKyAxCiAgICAjIHNwbGl0IHBhcmFtcyBzdHJpbmcKICAgIHBhcmFtX3N0ciA9IG5hbWVzKHRhcmdldF9maWxlcylbY291bnRlcl9jbHVtcF0gJT4lIAogICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIl8iLCBzaW1wbGlmeSA9IFQpICU+JQogICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIi0iLCBzaW1wbGlmeSA9IFQpCiAgICAjIGdldCBwYXJhbXMKICAgIHIyID0gYXMubnVtZXJpYyhwYXJhbV9zdHJbMSwyXSkKICAgIGtiID0gYXMuaW50ZWdlcihwYXJhbV9zdHJbMiwyXSkKICAgICMgcmVhZCBmaWxlcwogICAgZGYgPSByZWFkLnRhYmxlKHBhcmFtcywgaGVhZGVyID0gVCkKICAgICMgYWRkIHBhcmFtcyB0byBERgogICAgZGYkcjIgPSByMgogICAgZGYka2IgPSBrYgogICAgCiAgICByZXR1cm4oZGYpCiAgfSkKICAjIGFkZCBgY2x1bXBlZGAgbGlzdCB0byBgZGF0YV9saXN0YAogIHBoZW5vW1siY2x1bXBlZCJdXSA9IGNsdW1wZWQKICAjIGJpbmQgYGNsdW1wZWRgIGludG8gc2luZ2xlIERGIGFuZCBhZGQgdG8gYGRhdGFfbGlzdGAKICBwaGVub1tbImNsdW1wZWRfYWxsIl1dID0gZHBseXI6OmJpbmRfcm93cyhjbHVtcGVkKQogIAogIHJldHVybihwaGVubykKfSkKYGBgCgojIyBFeHRyYWN0IHJhbmRvbSBTTlBzIGZpbHRlcmVkIGJ5IHRob3NlIG1hdGNoaW5nIHRoZSBjbHVtcGVkIFNOUHMKCmBgYHtyLCBldmFsID0gRn0Kb3V0X2RpciA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjhfcmFuZG9tX3NucHMiKQoKZGlyLmNyZWF0ZShvdXRfZGlyKQoKY291bnRlciA9IDAKb3V0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgc2V0IGNvdW50ZXIKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICMgZ2V0IG5hbWUgb2YgdHJhaXQKICB0cmFpdCA9IG5hbWVzKGRhdGFfbGlzdClbY291bnRlcl0KICAjIGdldCBjbHVtcGVkIFNOUHMKICBjbHVtcGVkX3NucHMgPSBwaGVub1tbImNsdW1wZWQiXV1bW2NsdW1wX3BhcmFtXV0kU05QCiAgIyBnYXRoZXIgU05QcwogIHNucHMgPSBwaGVub1tbInJhbmRvbV9hZiJdXSAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKFRPUF9TTlAgJWluJSBjbHVtcGVkX3NucHMpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoUkFORE9NX1NOUCkgJT4lIAogICAgdW5pcXVlKC4pCiAgIyB3cml0ZSB0byBmaWxlCiAgb3V0X3BhdGggPSBmaWxlLnBhdGgob3V0X2RpciwgcGFzdGUodHJhaXQsICIubGlzdCIsIHNlcCA9ICIiKSkKICByZWFkcjo6d3JpdGVfbGluZXMoc25wcyRSQU5ET01fU05QLCBvdXRfcGF0aCkKfSkKcm0ob3V0KQpgYGAKCiMjIE1ha2UgVkNGcyBmb3IgcmFuZG9tIGNvbnRyb2wgU05QcwoKYGBge2Jhc2gsIGV2YWwgPSBGfQojIE9uIGNsdXN0ZXIKCiMgU2V0IHZhcmlhYmxlcwp0cmFpdHM9JChlY2hvIGhlaSBibWkgZWR1IGludCBpYmQgcGlnKQppbl9kaXJfc25wX2xpc3Q9ZGF0YS8yMDIxMDEyOF9yYW5kb21fc25wcwppbl92Y2Y9Li4vdmNmcy8xa2dfYWxsLnZjZi5negpyZWY9Li4vcmVmcy9oczM3ZDUuZmEuZ3oKb3V0X2Rpcl92Y2ZzPWRhdGEvMjAyMTAxMjhfcm5kbV92Y2ZzCgpta2RpciAtcCAkb3V0X2Rpcl92Y2ZzCgojIE1ha2UgbmV3IFZDRnMKZm9yIHRyYWl0IGluICQoZWNobyAkdHJhaXRzICk7IGRvCiAgYnN1YiBcCiAgICAtTSAyMDAwMCBcCiAgICAtbyAuLi9sb2cvMjAyMTAxMjhfZmlsdGVyX3ZjZnNfcm5kbV8kdHJhaXQub3V0IFwKICAgIC1lIC4uL2xvZy8yMDIxMDEyOF9maWx0ZXJfdmNmc19ybmRtXyR0cmFpdC5lcnIgXAogICAgIiIiCiAgICBjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwgOwogICAgZ2F0ayBTZWxlY3RWYXJpYW50cyBcCiAgICAgIC1SICRyZWYgXAogICAgICAtViAkaW5fdmNmIFwKICAgICAgLS1rZWVwLWlkcyAkaW5fZGlyX3NucF9saXN0LyR0cmFpdC5saXN0IFwKICAgICAgLU8gJG91dF9kaXJfdmNmcy8kdHJhaXQudmNmLmd6ICAgICAKICAgICIiIiA7CmRvbmUgICAgCmBgYAoKCiMjIENvbnNvbGlkYXRlIGtleSBkYXRhIGludG8gc2luZ2xlIERGIGZvciBhbmFseXNpcwoKKipOT1RFKio6IGBjbHVtcF9wYXJhbWAgaXMgc2V0IGluIGBjb2RlL3NjcmlwdHMvc291cmNlLlJgCgpgYGB7cn0KZGF0YV9saXN0ID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogICMgRmlsdGVyIGBjb25zb2xgIGJ5IHRoZSBpbmRleCBTTlBzIGluIHRhcmdldCBgY2x1bXBgCiAgdGFyZ2V0X2NsdW1wID0gcGhlbm9bWyJjbHVtcGVkIl1dW1tjbHVtcF9wYXJhbV1dCiAgCiAgZmluYWwgPSBwaGVub1tbImNvbnNvbCJdXSAlPiUgCiAgICBkcGx5cjo6cmVuYW1lKFNOUCA9IFRPUF9TTlApICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoU05QICVpbiUgdGFyZ2V0X2NsdW1wJFNOUCkgCiAgCiAgIyBBZGQgYWxsZWxlIGZyZXF1ZW5jaWVzIG9mIFNOUCBoaXRzIChnbG9iYWwgYW5kIHBlci1wb3B1bGF0aW9uKQogIAogICMgQWRkIGNvbnRyb2xzCiAgY29udHJvbHMgPSBwaGVub1tbInJhbmRvbV9hZiJdXSAlPiUKICAgICMgZmlsdGVyIGZvciBTTlBzIGluIHRhcmdldF9jbHVtcAogICAgZHBseXI6OmZpbHRlcihUT1BfU05QICVpbiUgdGFyZ2V0X2NsdW1wJFNOUCkgJT4lIAogICAgZHBseXI6OnNlbGVjdCgtYyhUT1BfU05QLCBCSU5fMTAwLCBSQU5ET01fQklOXzEwMCksIAogICAgICAgICAgICAgICAgICBTTlAgPSBSQU5ET01fU05QKSAlPiUgCiAgICBkcGx5cjo6bXV0YXRlKFJJU0tfQUYgPSBBTFRfRlJFUVMpCiAgCiAgZmluYWwgPSBkcGx5cjo6YmluZF9yb3dzKGZpbmFsLCBjb250cm9scykgIAogIHBoZW5vW1siZmluYWwiXV0gPSBmaW5hbAogIAogIHJldHVybihwaGVubykKfSkKCiMgQ3JlYXRlIGZpbmFsIGRmCmZpbmFsX2RmID0gbGFwcGx5KGRhdGFfbGlzdCwgZnVuY3Rpb24ocGhlbm8pewogIG91dCA9IHBoZW5vW1siZmluYWwiXV0KICAKICByZXR1cm4ob3V0KQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cygpCgojIFNldCBmYWN0b3JzCmZpbmFsX2RmJFBIRU5PIDwtIGZhY3RvcihmaW5hbF9kZiRQSEVOTywgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmIpCmZpbmFsX2RmJEhJVF9DT05UUk9MID0gZmFjdG9yKGZpbmFsX2RmJEhJVF9DT05UUk9MLCBsZXZlbHMgPSBoaXRfY29udHJvbF9sZXZlbHMpCgojIENyZWF0ZSBERiBmb3IgcGxvdHRpbmcKZmluYWxfcGx0ID0gZmluYWxfZGYgJT4lIAogIGRwbHlyOjpzZWxlY3QoU05QLCBQSEVOTywgUE9QTiwgUklTS19BRiwgSElUX0NPTlRST0wpICU+JSAKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFBPUE4sIHZhbHVlc19mcm9tID0gUklTS19BRikgCgpgYGAKCgojIEFuYWx5c2lzCgojIyBNYW5oYXR0YW4gcGxvdHMKCmBgYHtyLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICJoaWRlIn0KY291bnRlciA9IDAKbGFwcGx5KHVuaXF1ZShmaW5hbF9kZiRQSEVOTyksIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBjb3VudGVyCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBkZiA9IGZpbmFsX2RmICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoUEhFTk8gPT0gcGhlbm8gJiBISVRfQ09OVFJPTCA9PSAiaGl0IikKICAjIEdldCBudW1iZXIgb2YgU05QcwogIHNucF9uID0gbGVuZ3RoKHVuaXF1ZShkZiRTTlApKQogICMgR2V0IHRpdGxlCiAgdGl0bGUgPC0gcGFzdGUocGhlbm8sICJcbiIsICJTTlAgY291bnQ6Iiwgc25wX24pCiAgIyBQbG90CiAgZ2V0X21hbihkZiwgdHJhaXQgPSBwaGVubywgdGl0bGUgPSB0aXRsZSwgY2hyID0gIkNIUiIsIGJwID0gIlBPUyIsIHNucCA9ICJTTlAiLCBwID0gIlAiKQp9KQpgYGAKCiMjIEFsbGVsZSBmcmVxdWVuY3kgZGlzdHJpYnV0aW9ucyBvZiByaXNrIGFsbGVsZXMKCmBgYHtyfQpmaW5hbF9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKEhJVF9DT05UUk9MID09ICJoaXQiKSAlPiUgCiAgZ2dwbG90KGFlcyhSSVNLX0FGLCBmaWxsID0gUEhFTk8pKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgKwogICAgZmFjZXRfd3JhcCh2YXJzKFBIRU5PKSwgbnJvdyA9IDIpICsKICAgIGd1aWRlcyhmaWxsID0gRikgKwogICAgeGxhYigiUmlzayBhbGxlbGUgZnJlcXVlbmN5IikgKwogICAgeWxhYigiQ291bnQiKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIkZyZXF1ZW5jeSBkaXN0cmlidXRpb24gb2YgXCJyaXNrXCIgYWxsZWxlcyAoYWxsIDFLRyBwb3B1bGF0aW9ucyBjb21iaW5lZCkiKQoKCmBgYApgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19hZl9kaXN0cmlidXRpb24iLCAiMjAyMTAxMjdfaGl0c19hbGwucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgojIyBBbGxlbGUgZnJlcXVlbmN5IHZzIGVmZmVjdCBzaXplCgpgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiRnVsbCBzaXplIGFuZCB6b29tZWQgdG8gMCA8IHkgPCAyMCJ9Cm9uZSA9IGZpbmFsX2RmICU+JSAKICBkcGx5cjo6ZmlsdGVyKEhJVF9DT05UUk9MID09ICJoaXQiICYgUE9QTiA9PSAiYWxsIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKFJJU0tfQUYsIE9SX09SX0JFVEEsIGNvbG91ciA9IFBIRU5PKSwKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIGZhY2V0X3dyYXAodmFycyhQSEVOTyksIG5yb3cgPSAyKSArCiAgICBndWlkZXMoY29sb3VyID0gRiwgYWxwaGEgPSBGKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIlJpc2sgYWxsZWxlIGZyZXF1ZW5jeSB2cyBlZmZlY3Qgc2l6ZSAoT1Igb3IgYmV0YSkiKQoKIyBab29tIGluCnR3byA9IGZpbmFsX2RmICU+JSAKICBkcGx5cjo6ZmlsdGVyKEhJVF9DT05UUk9MID09ICJoaXQiICYgUE9QTiA9PSAiYWxsIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKFJJU0tfQUYsIE9SX09SX0JFVEEsIGNvbG91ciA9IFBIRU5PKSwKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIGZhY2V0X3dyYXAodmFycyhQSEVOTyksIG5yb3cgPSAyKSArCiAgICBndWlkZXMoY29sb3VyID0gRiwgYWxwaGEgPSBGKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIlJpc2sgYWxsZWxlIGZyZXF1ZW5jeSB2cyBlZmZlY3Qgc2l6ZSAoT1Igb3IgYmV0YSkiKSArCiAgICB5bGltKDAsMjApCgpvbmUKdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGLCBlY2hvID0gRn0Kb25lCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfYWZfdl9lZmZlY3Rfc2l6ZSIsICIyMDIxMDEyN19oaXRzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CnR3bwpgYGAKCmBgYHtyLCBldmFsID0gRn0KZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2FmX3ZfZWZmZWN0X3NpemUiLCAiMjAyMTAxMjdfaGl0c196b29tZWQucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgojIyBGc3QKCiMjIyBXaXRoIGFsbCBwb3B1bGF0aW9ucwoKIyMjIyBHZXQgRnN0IHN0YXRzIGZvciB0b3AgU05QcwoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCByZXN1bHRzPUYsIGV2YWwgPSBGfQojIENyZWF0ZSByYXcgbGlzdCBvZiB2YXJpYW50cwpoaXQgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI4X2hpdHNfdmNmcyIpCmNvbnRyb2wgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI4X3JuZG1fdmNmcyIpCnRhcmdldF9kaXJzID0gYyhoaXQsIGNvbnRyb2wpCm5hbWVzKHRhcmdldF9kaXJzKSA9IGMoImhpdCIsICJjb250cm9sIikKCiMgU2V0IHNlZWQKI2luaXRpYWxfc2VlZCA9IDUzCiNzZXQuc2VlZChpbml0aWFsX3NlZWQpCiNzZWVkcyA9IHNhbXBsZSgxOjEwMDAsIHNpemUgPSBsZW5ndGgodGFyZ2V0X2RpcnMpKQoKIyBSdW4gCmNvdW50ZXIgPSAwCmZzdF9vdXQgPSBsYXBwbHkodGFyZ2V0X2RpcnMsIGZ1bmN0aW9uKHRhcmdldF9kaXIpewogICMgc2V0IGNvdW50ZXIgCiAgY291bnRlciA8PC0gY291bnRlciArIDEKICAKICB2Y2ZfbGlzdF9yYXcgPC0gbGFwcGx5KHRyYWl0X2xldmVscywgZnVuY3Rpb24odHJhaXQpewogICAgdGFyZ2V0X2ZpbGUgPSBmaWxlLnBhdGgodGFyZ2V0X2RpciwgcGFzdGUodHJhaXQsICIudmNmLmd6Iiwgc2VwID0gIiIpKQogICAgIyByZWFkIGluIGZpbGUKICAgIHZjZl9vdXQgPC0gcGVnYXM6OnJlYWQudmNmKHRhcmdldF9maWxlKQogICAgCiAgICByZXR1cm4odmNmX291dCkKICB9KQogIAogICMgQ3JlYXRlIHZlY3RvciBvZiBwb3B1bGF0aW9ucwogIHBvcHVsYXRpb25zIDwtIHVubGlzdChsYXBwbHkocm93bmFtZXModmNmX2xpc3RfcmF3W1sxXV0pLCBmdW5jdGlvbihzYW1wbGUpewogICAgbWV0YSRQb3B1bGF0aW9uW21ldGEkU2FtcGxlID09IHNhbXBsZV0KICB9KSkKICAKICAjIEdlbmVyYXRlIEZzdCBzdGF0cwogIGZzdF9vdXRfZGYgPC0gbGFwcGx5KHZjZl9saXN0X3JhdywgZnVuY3Rpb24ocGhlbm8pewogICAgb3V0ID0gYXMuZGF0YS5mcmFtZShwZWdhczo6RnN0KHBoZW5vLCBwb3AgPSBwb3B1bGF0aW9ucykpCiAgICAjIHB1dCByb3duYW1lcyBpbnRvIHNlcGFyYXRlIGNvbHVtbgogICAgb3V0JHNucCA8LSByb3duYW1lcyhvdXQpCiAgICAKICAgIHJldHVybihvdXQpCiAgfSkgJT4lIAogICAgIyBiaW5kIGludG8gc2luZ2xlIERGCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJwaGVub3R5cGUiKSAlPiUgCiAgICAjIHJlbW92ZSBOQQogICAgdGlkeXI6OmRyb3BfbmEoKQogIAogIHJldHVybihmc3Rfb3V0X2RmKQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiaGl0X2NvbnRyb2wiKQoKIyBSZWNvZGUgcGhlbm90eXBlCmZzdF9vdXQkcGhlbm90eXBlIDwtIGZhY3Rvcihmc3Rfb3V0JHBoZW5vdHlwZSwgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzKQpmc3Rfb3V0JHBoZW5vdHlwZSA9IGRwbHlyOjpyZWNvZGUoZnN0X291dCRwaGVub3R5cGUsICEhIXJlY29kZV92ZWMpCmBgYAoKV3JpdGUgdG8gZmlsZQoKYGBge3IsIGV2YWwgPSBGfQpvdXRfZGlyID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyN19yZXN1bHRzIikKb3V0X3BhdGggPSBmaWxlLnBhdGgob3V0X2RpciwgcGFzdGUoIjIwMjEwMTI4X2ZzdCIsICIuY3N2Iiwgc2VwID0gIiIpKQoKZGlyLmNyZWF0ZShvdXRfZGlyKQoKcmVhZHI6OndyaXRlX2Nzdihmc3Rfb3V0LCBvdXRfcGF0aCkKYGBgCgpSZWFkIGJhY2sgaW4gCmBgYHtyLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICdhc2lzJ30KZnN0X291dCA9IHJlYWRyOjpyZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI3X3Jlc3VsdHMvMjAyMTAxMjhfZnN0LmNzdiIpKQpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKQoKa25pdHI6OmthYmxlKGhlYWQoZnN0X291dCkpCmBgYAoKIyMjIyAqRnN0KiBoaXN0b2dyYW1zCgpgYGB7cixmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcCA9ICJIaXRzIHZzIGNvbnRyb2xzIn0Kb25lID0gZnN0X291dCAlPiUKICBkcGx5cjo6ZmlsdGVyKGhpdF9jb250cm9sID09ICJoaXQiKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSksIGJpbnMgPSAxMDApICsKICAgIGZhY2V0X3dyYXAofnBoZW5vdHlwZSkgKwogICAgdGhlbWVfYncoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgICsKICAgIGd1aWRlcyhmaWxsID0gRikKCnR3byA9IGZzdF9vdXQgJT4lCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSwgYmlucyA9IDEwMCkgKwogICAgZmFjZXRfd3JhcCh+cGhlbm90eXBlKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9zZWNvbmRhcnkpICsKICAgIGd1aWRlcyhmaWxsID0gRikKCm9uZQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gID0gRn0Kb25lCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfaGlzdG9ncmFtcyIsICIyMDIxMDEyOF9oaXRzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyAgPSBGfQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19oaXN0b2dyYW1zIiwgIjIwMjEwMTI4X2NvbnRyb2xzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCiMjIyMgKkZzdCogZGVuc2l0eQoKIyMjIyMgRmFjZXRzCgpgYGB7ciwgd2FybmluZz0gRiwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiSGl0cyB2cyBjb250cm9scyJ9Cm9uZSA9IGZzdF9vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHlsYWIoIkRlbnNpdHkiKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSArCiAgICBndWlkZXMoZmlsbCA9IEYpCgoKdHdvID0gZnN0X291dCAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHlsYWIoIkRlbnNpdHkiKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9zZWNvbmRhcnkpICsKICAgIGd1aWRlcyhmaWxsID0gRikKCm9uZQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQpvbmUKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19kZW5zaXRpZXMiLCAiMjAyMTAxMjhfaGl0c19hbGwucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19kZW5zaXRpZXMiLCAiMjAyMTAxMjhfY29udHJvbHNfYWxsLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIyMgUmlkZ2VzCgpgYGB7ciwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcCA9ICJIaXRzIHZzIGNvbnRyb2xzIn0Kb25lID0gZnN0X291dCAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiaGl0IikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fZGVuc2l0eV9yaWRnZXMyKG1hcHBpbmcgPSBhZXMoeCA9IEZzdCwgeSA9IHBoZW5vdHlwZSwgZmlsbCA9IHBoZW5vdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IDIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSArCiAgICB5bGFiKGxhYmVsID0gTlVMTCkgKwogICAgdGhlbWVfYncoKSArCiAgICBndWlkZXMoZmlsbCA9IEYpICsKICAgIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGFkZCA9IGMoMC4yLCAyLjMpKSkKCnR3byA9IGZzdF9vdXQgJT4lCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzMihtYXBwaW5nID0gYWVzKHggPSBGc3QsIHkgPSBwaGVub3R5cGUsIGZpbGwgPSBwaGVub3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUgPSAyKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfc2Vjb25kYXJ5KSArCiAgICB5bGFiKGxhYmVsID0gTlVMTCkgKwogICAgdGhlbWVfYncoKSArCiAgICBndWlkZXMoZmlsbCA9IEYpICsKICAgIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGFkZCA9IGMoMC4yLCAyLjMpKSkKCm9uZQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQpvbmUKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19yaWRnZXMiLCAiMjAyMTAxMjhfaGl0c19hbGwucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gPSBGfQp0d28KYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19yaWRnZXMiLCAiMjAyMTAxMjhfY29udHJvbHNfYWxsLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIFJ1biBLb2xtb2dvcm92LVNtaXJub3YgVGVzdHMKCmBgYHtyLCB3YXJuaW5nID0gRn0KaGl0X2NvbnRyb2wgPSB1bmlxdWUoZnN0X291dCRoaXRfY29udHJvbCkKbmFtZXMoaGl0X2NvbnRyb2wpID0gaGl0X2NvbnRyb2wKCmtzX291dCA9IGxhcHBseShoaXRfY29udHJvbCwgZnVuY3Rpb24oZGF0YXNldCl7CiAgIyBmaWx0ZXIgZGF0YXNldAogIHRhcmdldF9kZiA9IGZzdF9vdXRbZnN0X291dCRoaXRfY29udHJvbCA9PSBkYXRhc2V0LCBdCiAgIyBydW4gcGFpcndpc2UgS1MgdGVzdHMKICBrc19vdXQgPSBsYXBwbHkodHJhaXRfbGV2ZWxzX3ZlcmIsIGZ1bmN0aW9uKHRyYWl0X2EpewogICAgb3V0ID0gbGFwcGx5KHRyYWl0X2xldmVsc192ZXJiLCBmdW5jdGlvbih0cmFpdF9iKXsKICAgICAgcmVzdWx0cyA9IGtzLnRlc3QodGFyZ2V0X2RmJEZzdFt0YXJnZXRfZGYkcGhlbm90eXBlID09IHRyYWl0X2FdLAogICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfZGYkRnN0W3RhcmdldF9kZiRwaGVub3R5cGUgPT0gdHJhaXRfYl0pCiAgICAgIFAgPSByZXN1bHRzJHAudmFsdWUKICAgICAgCiAgICAgIHJldHVybihQKQogICAgfSkgJT4lIAogICAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJ0ZXN0X2IiKQogICAgCiAgICByZXR1cm4ob3V0KQogIH0pICAlPiUgCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJ0cmFpdCIpCiAgCiAgdHJhaXRzID0ga3Nfb3V0JHRyYWl0CiAga3Nfb3V0JHRyYWl0IDwtIE5VTEwKICAKICByb3duYW1lcyhrc19vdXQpID0gdHJhaXRzCiAgcmV0dXJuKGtzX291dCkKfSkKCiMgY29udmVydCB0byBtYXRyaXgKa3NfbWF0ID0gbGFwcGx5KGtzX291dCwgZnVuY3Rpb24oZGF0YXNldCl7CiAgb3V0ID0gYXMubWF0cml4KGRhdGFzZXQpCiAgCiAgcmV0dXJuKG91dCkKfSkKYGBgCgoKYGBge3J9CiMgUHJvY2VzcyBmb3IgcGxvdHRpbmcKa3Nfb3V0X2dnID0gbGFwcGx5KGtzX291dCwgZnVuY3Rpb24oZGF0YXNldCl7CiAgb3V0ID0gZGF0YXNldAogIG91dCRBID0gcm93bmFtZXMoZGF0YXNldCkKICBvdXQgPSBvdXQgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtQSwgbmFtZXNfdG8gPSAiQiIsIHZhbHVlc190byA9ICJrc19QIikKICAjIGNvbnZlcnQgUC12YWx1ZXMgdG8gLWxvZzEwCiAgb3V0JGtzX1AgPSAtbG9nMTAob3V0JGtzX1ApCiAgCiAgcmV0dXJuKG91dCkKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gImhpdF9jb250cm9sIikgJT4lIAogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGMoIkEiLCAiQiIpLAogICAgICAgICAgICAgICAgICAgICAgIH5mYWN0b3IoLngsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKSkpCmBgYAoKUGxvdApgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiSGl0cyB2cyBjb250cm9scyJ9CmhlYXRfaGl0c19hbGwgPSBrc19vdXRfZ2cgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgICBjb29yZF9maXhlZCgpICsKICAgIHhsYWIoTlVMTCkgKwogICAgeWxhYihOVUxMKSArCiAgICBsYWJzKGZpbGwgPSAiS1MtdGVzdFxuLWxvZyhQKSIpCgpoZWF0X2NvbnRyb2xzX2FsbCA9IGtzX291dF9nZyAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIktTLXRlc3Rcbi1sb2coUCkiKQoKaGVhdF9oaXRzX2FsbApoZWF0X2NvbnRyb2xzX2FsbApgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfaGl0c19hbGwKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19rc19oZWF0bWFwcyIsICIyMDIxMDEyN19oaXRzX2FsbC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfY29udHJvbHNfYWxsCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfa3NfaGVhdG1hcHMiLCAiMjAyMTAxMjdfY29udHJvbHNfYWxsLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIFRyeSB3aXRoIHNhbWUgbnVtYmVyIG9mIFNOUHMgCgpbKipTdHJhdGVneSoqXXtjb2xvcj0icmVkIn06IFdpbGwgaXQgbWFrZSBhIGRpZmZlcmVuY2UgaWYgd2UgYW5hbHlzZSB0aGUgc2FtZSBudW1iZXIgb2YgU05QcyBmb3IgZWFjaCB0cmFpdD8gaS5lLiBpcyB0aGUgZGlmZmVyZW50IG51bWJlciBvZiBTTlBzIGZvciBlYWNoIHRyYWl0IGFmZmVjdGluZyB0aGUgS1MgdGVzdCBvdXRwdXQ/CgpJQkQgb25seSBoYXMgMTgyIGhpdHMsIHNvIHJlbW92ZSBmcm9tIHRoaXMgcGFydCBvZiB0aGUgYW5hbHlzaXMuCgpUaGUgdHJhaXQgd2l0aCB0aGUgbmV4dCBsZWFzdCBudW1iZXIgb2YgaGl0cyBpcyBgSW50ZWxsaWdlbmNlYCwgd2l0aCBgNDgwLiBTbyB3ZSdsbCB0YWtlIGEgcmFuZG9tIHNhbXBsZSBvZiA0MAoKYGBge3J9CmZzdF9zYW1wbGUgPSBzcGxpdChmc3Rfb3V0LCBmID0gZnN0X291dCRoaXRfY29udHJvbCkKZnN0X3NhbXBsZSA9IGxhcHBseShmc3Rfc2FtcGxlLCBmdW5jdGlvbihkYXRhc2V0KXsKICBvdXQgPSBzcGxpdChkYXRhc2V0LCBmID0gZGF0YXNldCRwaGVub3R5cGUpCiAgb3V0ID0gbGFwcGx5KG91dCwgZnVuY3Rpb24ocGhlbm8pewogICAgcGhlbm8gPSBwaGVubyAlPiUgCiAgICAgIGRwbHlyOjpzbGljZV9zYW1wbGUobiA9IDQ4MCkKICAgIAogICAgcmV0dXJuKHBoZW5vKQogIH0pICU+JSAKICAgIGRwbHlyOjpiaW5kX3Jvd3MoKSAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKHBoZW5vdHlwZSAhPSAiSUJEIikKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoKQpgYGAKCiMjIyMgSGlzdG9ncmFtcwoKYGBge3IsIGZpZy5zaG93PSJob2xkIiwgb3V0LndpZHRoPSc1MCUnLCBmaWcuY2FwID0gIlNBTVBMRUQ6IEhpdHMgdnMgY29udHJvbHMifQpvbmUgPSBmc3Rfc2FtcGxlICU+JQogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSwgYmlucyA9IDEwMCkgKwogICAgZmFjZXRfd3JhcCh+cGhlbm90eXBlKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSAgKwogICAgZ3VpZGVzKGZpbGwgPSBGKQoKdHdvID0gZnN0X3NhbXBsZSAlPiUKICBkcGx5cjo6ZmlsdGVyKGhpdF9jb250cm9sID09ICJjb250cm9sIikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyhGc3QsIGZpbGwgPSBwaGVub3R5cGUpLCBiaW5zID0gMTAwKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHRoZW1lX2J3KCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3NlY29uZGFyeSkgKwogICAgZ3VpZGVzKGZpbGwgPSBGKQoKb25lCnR3bwpgYGAKYGBge3IsIGV2YWwgPSBGLCBlY2hvICA9IEZ9Cm9uZQpgYGAKCmBgYHtyLCBldmFsID0gRn0KZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2hpc3RvZ3JhbXMiLCAiMjAyMTAxMjdfaGl0c19zYW1wbGUucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgpgYGB7ciwgZXZhbCA9IEYsIGVjaG8gID0gRn0KdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfaGlzdG9ncmFtcyIsICIyMDIxMDEyN19jb250cm9sc19zYW1wbGUucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwKICAgICAgIHVuaXRzID0gImNtIiwKICAgICAgIGRwaSA9IDQwMCwKICAgICAgIGhlaWdodCA9IDEyLAogICAgICAgd2lkdGggPSAyMCkKYGBgCgojIyMjIERlbnNpdHkKCmBgYHtyLCB3YXJuaW5nPSBGLCBmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcCA9ICJTQU1QTEVEOiBIaXRzIHZzIGNvbnRyb2xzIn0Kb25lID0gZnN0X3NhbXBsZSAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiaGl0IikgJT4lIAogIGdncGxvdChhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgbGFicyhmaWxsID0gIlBoZW5vdHlwZSIpICsKICAgIGZhY2V0X3dyYXAofnBoZW5vdHlwZSkgKwogICAgeWxhYigiRGVuc2l0eSIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIGd1aWRlcyhmaWxsID0gRikKCnR3byA9IGZzdF9zYW1wbGUgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImNvbnRyb2wiKSAlPiUgCiAgZ2dwbG90KGFlcyhGc3QsIGZpbGwgPSBwaGVub3R5cGUpKSArCiAgICBnZW9tX2RlbnNpdHkoKSArCiAgICBsYWJzKGZpbGwgPSAiUGhlbm90eXBlIikgKwogICAgZmFjZXRfd3JhcCh+cGhlbm90eXBlKSArCiAgICB5bGFiKCJEZW5zaXR5IikgKwogICAgdGhlbWVfYncoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfc2Vjb25kYXJ5KSArCiAgICBndWlkZXMoZmlsbCA9IEYpCgpvbmUKdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGLCBlY2hvID0gRn0Kb25lCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfZGVuc2l0aWVzIiwgIjIwMjEwMTI3X2hpdHNfc2FtcGxlLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKYGBge3IsIGV2YWwgPSBGLCBlY2hvID0gRn0KdHdvCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfZGVuc2l0aWVzIiwgIjIwMjEwMTI3X2NvbnRyb2xzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCiMjIyBLUyB0ZXN0CgpgYGB7ciwgd2FybmluZyA9IEZ9CmhpdF9jb250cm9sID0gdW5pcXVlKGZzdF9zYW1wbGUkaGl0X2NvbnRyb2wpCm5hbWVzKGhpdF9jb250cm9sKSA9IGhpdF9jb250cm9sCgp0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUgPSB0cmFpdF9sZXZlbHNfdmVyYlstd2hpY2godHJhaXRfbGV2ZWxzX3ZlcmIgPT0gIklCRCIpXQp0cmFpdHNfc2FtcGxlID0gdHJhaXRzWy13aGljaCh0cmFpdHMgPT0gImliZCIpXQoKa3Nfc2FtcGxlID0gbGFwcGx5KGhpdF9jb250cm9sLCBmdW5jdGlvbihkYXRhc2V0KXsKICAjIGZpbHRlciBkYXRhc2V0CiAgdGFyZ2V0X2RmID0gZnN0X3NhbXBsZVtmc3Rfc2FtcGxlJGhpdF9jb250cm9sID09IGRhdGFzZXQsIF0KICAjIHJ1biBwYWlyd2lzZSBLUyB0ZXN0cwogIGtzX291dCA9IGxhcHBseSh0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUsIGZ1bmN0aW9uKHRyYWl0X2EpewogICAgb3V0ID0gbGFwcGx5KHRyYWl0X2xldmVsc192ZXJiX3NhbXBsZSwgZnVuY3Rpb24odHJhaXRfYil7CiAgICAgIHJlc3VsdHMgPSBrcy50ZXN0KHRhcmdldF9kZiRGc3RbdGFyZ2V0X2RmJHBoZW5vdHlwZSA9PSB0cmFpdF9hXSwKICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0X2RmJEZzdFt0YXJnZXRfZGYkcGhlbm90eXBlID09IHRyYWl0X2JdKQogICAgICBQID0gcmVzdWx0cyRwLnZhbHVlCiAgICAgIAogICAgICByZXR1cm4oUCkKICAgIH0pICU+JSAKICAgICAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAidGVzdF9iIikKICAgIAogICAgcmV0dXJuKG91dCkKICB9KSAgJT4lIAogICAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAidHJhaXQiKQogIAogIHRyYWl0cyA9IGtzX291dCR0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUKICBrc19vdXQkdHJhaXQgPC0gTlVMTAogIAogIHJvd25hbWVzKGtzX291dCkgPSB0cmFpdF9sZXZlbHNfdmVyYl9zYW1wbGUKICByZXR1cm4oa3Nfb3V0KQp9KQpgYGAKCiMjIyMgSGVhdG1hcHMKCmBgYHtyfQprc19zYW1wbGVfZ2cgPSBsYXBwbHkoa3Nfc2FtcGxlLCBmdW5jdGlvbihkYXRhc2V0KXsKICBvdXQgPSBkYXRhc2V0CiAgb3V0JEEgPSByb3duYW1lcyhkYXRhc2V0KQogIG91dCA9IG91dCAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IC1BLCBuYW1lc190byA9ICJCIiwgdmFsdWVzX3RvID0gImtzX1AiKQogICMgY29udmVydCBQLXZhbHVlcyB0byAtbG9nMTAKICBvdXQka3NfUCA9IC1sb2cxMChvdXQka3NfUCkKICAKICByZXR1cm4ob3V0KQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAiaGl0X2NvbnRyb2wiKSAlPiUgCiAgZHBseXI6Om11dGF0ZShhY3Jvc3MoYygiQSIsICJCIiksCiAgICAgICAgICAgICAgICAgICAgICAgfmZhY3RvcigueCwgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmJfc2FtcGxlKSkpCmBgYAoKUGxvdApgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiU0FNUExFRDogSGl0cyB2cyBjb250cm9scyJ9CmhlYXRfaGl0c19zYW1wbGUgPSBrc19zYW1wbGVfZ2cgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgICBjb29yZF9maXhlZCgpICsKICAgIHhsYWIoTlVMTCkgKwogICAgeWxhYihOVUxMKSArCiAgICBsYWJzKGZpbGwgPSAiS1MtdGVzdFxuLWxvZyhQKSIpCgpoZWF0X2NvbnRyb2xzX3NhbXBsZSA9IGtzX3NhbXBsZV9nZyAlPiUgCiAgZHBseXI6OmZpbHRlcihoaXRfY29udHJvbCA9PSAiY29udHJvbCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKEEsIEIsIGZpbGwgPSBrc19QKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIktTLXRlc3Rcbi1sb2coUCkiKQogICAKaGVhdF9oaXRzX3NhbXBsZQpoZWF0X2NvbnRyb2xzX3NhbXBsZQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfaGl0c19zYW1wbGUKYGBgCgpgYGB7ciwgZXZhbCA9IEZ9Cmdnc2F2ZShoZXJlKCJwbG90cyIsICIyMDIxMDEyN19rc19oZWF0bWFwcyIsICIyMDIxMDEyN19oaXRzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQpgYGAKCmBgYHtyLCBldmFsID0gRiwgZWNobyA9IEZ9CmhlYXRfY29udHJvbHNfc2FtcGxlCmBgYAoKYGBge3IsIGV2YWwgPSBGfQpnZ3NhdmUoaGVyZSgicGxvdHMiLCAiMjAyMTAxMjdfa3NfaGVhdG1hcHMiLCAiMjAyMTAxMjdfY29udHJvbHNfc2FtcGxlLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsCiAgICAgICB1bml0cyA9ICJjbSIsCiAgICAgICBkcGkgPSA0MDAsCiAgICAgICBoZWlnaHQgPSAxMiwKICAgICAgIHdpZHRoID0gMjApCmBgYAoKIyMjIENvbXBhcmUgZWZmZWN0cyBvZiBzYW1wbGluZyBvbiBQLXZhbHVlcwoKYGBge3IsIGZpZy5zaG93PSJob2xkIiwgb3V0LndpZHRoPSc1MCUnLCBmaWcuY2FwID0gIkhpdHM6IGFsbCB2cyBzYW1wbGUifQpoZWF0X2hpdHNfYWxsCmhlYXRfaGl0c19zYW1wbGUKYGBgCgpgYGB7ciwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiQ29udHJvbHM6IGFsbCB2cyBzYW1wbGUifQpoZWF0X2NvbnRyb2xzX2FsbApoZWF0X2NvbnRyb2xzX3NhbXBsZQpgYGAKCiMjIFRyeSBkaWZmZXJlbnQgd2F5cyBvZiB0ZXN0aW5nIGRpZmZlcmVuY2VzCgojIyMgUmVhZCBpbiAqRnN0KiByZXN1bHRzCmBgYHtyLCBtZXNzYWdlID0gRn0KZnN0X291dCA9IHJlYWRyOjpyZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI3X3Jlc3VsdHMvMjAyMTAxMjhfZnN0LmNzdiIpKQpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiWy0xXSkKCmBgYAoKIyMjIFJ1biBLb2xtb2dvcm92LVNtaXJub3YgVGVzdHMKCkZyb20gdGhlIGhlbHAgcGFnZXMgb2YgYGtzLnRlc3RgOgoKPiBUaGUgcG9zc2libGUgdmFsdWVzICJgdHdvLnNpZGVkYCIsICJgbGVzc2AiIGFuZCAiYGdyZWF0ZXJgIiBvZiBhbHRlcm5hdGl2ZSBzcGVjaWZ5IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgdHJ1ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gb2YgYHhgIGlzIGVxdWFsIHRvLCBub3QgbGVzcyB0aGFuIG9yIG5vdCBncmVhdGVyIHRoYW4gdGhlIGh5cG90aGVzaXplZCBkaXN0cmlidXRpb24gZnVuY3Rpb24gKG9uZS1zYW1wbGUgY2FzZSkgb3IgdGhlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiBvZiBgeWAgKHR3by1zYW1wbGUgY2FzZSksIHJlc3BlY3RpdmVseS4gVGhpcyBpcyBhIGNvbXBhcmlzb24gb2YgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb25zLCBhbmQgdGhlIHRlc3Qgc3RhdGlzdGljIGlzIHRoZSBtYXhpbXVtIGRpZmZlcmVuY2UgaW4gdmFsdWUsIHdpdGggdGhlIHN0YXRpc3RpYyBpbiB0aGUgYCJncmVhdGVyImAgYWx0ZXJuYXRpdmUgYmVpbmcgKkReKyA9IG1heFtGX3godSkgLSBGX3kodSldKi4gVGh1cyBpbiB0aGUgdHdvLXNhbXBsZSBjYXNlIGBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyImAgaW5jbHVkZXMgZGlzdHJpYnV0aW9ucyBmb3Igd2hpY2ggeCBpcyBzdG9jaGFzdGljYWxseSBzbWFsbGVyIHRoYW4geSAodGhlIENERiBvZiB4IGxpZXMgYWJvdmUgYW5kIGhlbmNlIHRvIHRoZSBsZWZ0IG9mIHRoYXQgZm9yIHkpLCBpbiBjb250cmFzdCB0byB0LnRlc3Qgb3Igd2lsY294LnRlc3QuCgpgYGB7ciwgd2FybmluZyA9IEZ9CmtzX291dCA9IHNwbGl0KGZzdF9vdXQsIGYgPSBmc3Rfb3V0JGhpdF9jb250cm9sKQprc19vdXQgPSBsYXBwbHkoa3Nfb3V0LCBmdW5jdGlvbihkYXRhc2V0KXsKICBzcGxpdF9kYXRhID0gc3BsaXQoZGF0YXNldCwgZiA9IGRhdGFzZXQkcGhlbm90eXBlKQogICMgcnVuIHBhaXJ3aXNlIEtTIHRlc3RzCiAga3Nfb3V0ID0gbGFwcGx5KHNwbGl0X2RhdGEsIGZ1bmN0aW9uKHRyYWl0X2EpewogICAgb3V0ID0gbGFwcGx5KHNwbGl0X2RhdGEsIGZ1bmN0aW9uKHRyYWl0X2IpewogICAgICAjICJ0d28uc2lkZWQiCiAgICAgIHJlc3VsdHNfdHdvX3NpZGVkID0ga3MudGVzdCh0cmFpdF9hJEZzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0X2IkRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIikgICAgICAKICAgICAgIyAibGVzcyIgdGVzdAogICAgICByZXN1bHRzX2xlc3MgPSBrcy50ZXN0KHRyYWl0X2EkRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0X2IkRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gImxlc3MiKQogICAgICAjICJncmVhdGVyIiB0ZXN0CiAgICAgIHJlc3VsdHNfZ3JlYXRlciA9IGtzLnRlc3QodHJhaXRfYSRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRfYiRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCiAgICAgICMgTWFubi1XaGl0bmV5IAogICAgICByZXN1bHRzX213ID0gd2lsY294LnRlc3QodHJhaXRfYSRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdF9iJEZzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCiAgICAgICMgYmluZCBpbnRvIERGCiAgICAgIGRmX291dCA9IGMocmVzdWx0c190d29fc2lkZWRbWyJzdGF0aXN0aWMiXV0sCiAgICAgICAgICAgICAgICAgIlBfVFdPX1NJREVEIiA9IHJlc3VsdHNfdHdvX3NpZGVkW1sicC52YWx1ZSJdXSwKICAgICAgICAgICAgICAgICByZXN1bHRzX2xlc3NbWyJzdGF0aXN0aWMiXV0sCiAgICAgICAgICAgICAgICAgIlBfTEVTUyIgPSByZXN1bHRzX2xlc3NbWyJwLnZhbHVlIl1dLAogICAgICAgICAgICAgICAgIHJlc3VsdHNfZ3JlYXRlcltbInN0YXRpc3RpYyJdXSwKICAgICAgICAgICAgICAgICAiUF9HUkVBVEVSIiA9IHJlc3VsdHNfZ3JlYXRlcltbInAudmFsdWUiXV0sCiAgICAgICAgICAgICAgICAgcmVzdWx0c19td1tbInN0YXRpc3RpYyJdXSwKICAgICAgICAgICAgICAgICAiUF9NVyIgPSByZXN1bHRzX213W1sicC52YWx1ZSJdXSkKCiAgICAgIHJldHVybihkZl9vdXQpCiAgICAgIHJldHVybihyZXN1bHRzX213KQogICAgfSkgJT4lIAogICAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJCIikKICB9KSAlPiUgCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJBIikKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gIkhJVF9DT05UUk9MIikgJT4lIAogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGMoIkEiLCAiQiIpLAogICAgICAgICAgICAgICAgICAgICAgIH5mYWN0b3IoLngsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKSkpCgoKYGBgCgojIyMgUGxvdCBLUy10ZXN0IEQKClNob3cgY29ycmVsYXRpb24KCmBgYHtyfQpwbHQgPSBrc19vdXQgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKGBEXitgLCBgRF4tYCwgY29sb3VyID0gSElUX0NPTlRST0wsCiAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoIlRyYWl0IEE6ICIsIEEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5UcmFpdCBCOiAiLCBCKSkpICsKICAgIHRoZW1lX2J3KCkgKwogICAgY29vcmRfZml4ZWQoKQoKZ2dwbG90bHkocGx0KQoKYGBgCgoKCmBgYHtyfQpnZ3Bsb3RseShwbHQpICU+JQogIGV4cG9ydChmaWxlID0gaGVyZTo6aGVyZSgicGxvdHMiLCAiMjAyMTAxMjlfS1MtRC5zdmciKSwKICAgICAgICAgc2VsZW5pdW0gPSBSU2VsZW5pdW06OnJzRHJpdmVyKGJyb3dzZXIgPSAiZmlyZWZveCIpKQpgYGAKCiMjIyBQbG90IE1XCgpgYGB7cn0KaGVhdF9oaXRzX3NhbXBsZSA9IGtzX291dCAlPiUgCiAgZHBseXI6OmZpbHRlcihISVRfQ09OVFJPTCA9PSAiaGl0IikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fdGlsZShhZXMoQSwgQiwgZmlsbCA9IC1sb2cxMChQX01XKSkpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIk1XLXRlc3Rcbi1sb2coVykiKQoKZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2tzX2hlYXRtYXBzIiwgIjIwMjEwMTI3X2NvbnRyb2xzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQoKaGVhdF9jb250cm9sc19zYW1wbGUgPSBrc19vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoSElUX0NPTlRST0wgPT0gImNvbnRyb2wiKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV90aWxlKGFlcyhBLCBCLCBmaWxsID0gLWxvZzEwKFBfTVcpKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICB4bGFiKE5VTEwpICsKICAgIHlsYWIoTlVMTCkgKwogICAgbGFicyhmaWxsID0gIk1XLXRlc3Rcbi1sb2coVykiKQoKZ2dzYXZlKGhlcmUoInBsb3RzIiwgIjIwMjEwMTI3X2tzX2hlYXRtYXBzIiwgIjIwMjEwMTI3X2NvbnRyb2xzX3NhbXBsZS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLAogICAgICAgdW5pdHMgPSAiY20iLAogICAgICAgZHBpID0gNDAwLAogICAgICAgaGVpZ2h0ID0gMTIsCiAgICAgICB3aWR0aCA9IDIwKQogICAKaGVhdF9oaXRzX3NhbXBsZQpoZWF0X2NvbnRyb2xzX3NhbXBsZQpgYGAKCiMjIENhbGN1bGF0ZSAqRnN0KiBmb3IgMTAwayByYW5kb20gU05QcyB0byBjb21wYXJlIHRvIHRoZXNlIGRpc3RyaWJ1dGlvbnMKCiMjIyBQdWxsIHJhbmRvbSBsaW5lcyBmcm9tIDFrZyBhbGZyZXEgZmlsZQoKYGBge2Jhc2h9CiMgT24gY2x1c3RlciAKCmxpc3RfaW49Li4vYmlnX2RhdGEvMjAyMTAxMjVfYWxmcmVxc19hbGxfYmlubmVkL2FsbC5hZnJlcQpsaXN0X291dD1kYXRhLzIwMjEwMTI5X3JhbmRvbV8xMDBrX3NucHMubGlzdApyZWY9Li4vcmVmcy9oczM3ZDUuZmEuZ3oKaW5fdmNmPS4uL3ZjZnMvMWtnX2FsbC52Y2YuZ3oKb3V0X3ZjZj0uLi92Y2ZzLzFrZ18xMDBrX3JuZG0udmNmLmd6Cgpjb25kYSBhY3RpdmF0ZSBmc3RfZW52X3JoZWwgCgojIGNyZWF0ZSBmdW5jdGlvbiB0byBnZXQgcmFuZG9tIHNlZWQKZ2V0X3NlZWRlZF9yYW5kb20oKQp7CiAgc2VlZD0iJDEiCiAgb3BlbnNzbCBlbmMgLWFlcy0yNTYtY3RyIC1wYXNzIHBhc3M6IiRzZWVkIiAtbm9zYWx0IFwKICAgIDwvZGV2L3plcm8gMj4vZGV2L251bGwKfQoKIyBtYWtlIGxpc3QKYXdrICd7cHJpbnQgJDJ9JyAkbGlzdF9pbiB8XAogIHRhaWwgLW4rMiB8XAogIHNodWYgLW4gMTAwMDAwIFwKICAtLXJhbmRvbS1zb3VyY2U9PChnZXRfc2VlZGVkX3JhbmRvbSA0NTQpIFwKICAgID4gJGxpc3Rfb3V0CiAgICAKIyBleHRyYWN0IGZyb20gMUtHCmdhdGsgU2VsZWN0VmFyaWFudHMgXAogIC1SICRyZWYgXAogIC1WICRpbl92Y2YgXAogIC0ta2VlcC1pZHMgJGxpc3Rfb3V0IFwKICAtTyAkb3V0X3ZjZgpgYGAKCiMjIyBSdW4gYHBlZ2FzYCB0byBnZXQgRnN0CgpgYGB7ciwgZXZhbCA9IEZ9CiMgT24gY2x1c3RlcgpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlOjpoZXJlKCJjb2RlIiwgInNjcmlwdHMiLCAic291cmNlLlIiKSkKCiMgU2V0IHZhcmlhYmxlcwppbl92Y2Y9aGVyZTo6aGVyZSgiLi4iLCAidmNmcyIsICIxa2dfMTAwa19ybmRtLnZjZi5neiIpCnNhbXBsZXNfZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxMzA2MDZfc2FtcGxlX2luZm8ueGxzeCIpCm91dF9maWxlPWhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjlfMTAwa19ybmRtX2ZzdC50eHQiKQoKIyBSZWFkIGluIGBtZXRhYCBmaWxlCm1ldGEgPSByZWFkeGw6OnJlYWRfeGxzeChzYW1wbGVzX2ZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9ICJTYW1wbGUgSW5mbyIpICU+JQogIGRwbHlyOjpzZWxlY3QoU2FtcGxlLCBQb3B1bGF0aW9uLCBHZW5kZXIpCgojIFJlYWQgVkNGIAp2Y2Zfb3V0IDwtIHBlZ2FzOjpyZWFkLnZjZihpbl92Y2YsIHRvID0gMTAwMDAwKQoKIyBDcmVhdGUgdmVjdG9yIG9mIHBvcHVsYXRpb25zCnBvcHVsYXRpb25zID0gdW5saXN0KGxhcHBseShyb3duYW1lcyh2Y2Zfb3V0KSwgZnVuY3Rpb24oc2FtcGxlKXsKICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQp9KSkKCiMgR2VuZXJhdGUgRnN0IHN0YXRzCmZzdF9vdXQgPC0gYXMuZGF0YS5mcmFtZShwZWdhczo6RnN0KHZjZl9vdXQsIHBvcCA9IHBvcHVsYXRpb25zKSkKZnN0X291dCRzbnAgPC0gcm93bmFtZXMoZnN0X291dCkKIyByZW1vdmUgTkFzCmZzdF9vdXQgID0gZnN0X291dCAlPiUgCiAgdGlkeXI6OmRyb3BfbmEoKSAKIyA5OTY4MiByZW1haW5pbmcKICAKIyBTZXQgcGhlbm90eXBlCmZzdF9vdXQkcGhlbm90eXBlIDwtICJSYW5kb20iCgojIFNhdmUgZmlsZQpyZWFkcjo6d3JpdGVfdHN2KGZzdF9vdXQsIG91dF9maWxlKQpgYGAKCiMjIyBSZWFkIGluCgpgYGB7cn0KIyByZWFkCmZzdF9vdXQgPSByZWFkcjo6cmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIxMDEyN19yZXN1bHRzLzIwMjEwMTI4X2ZzdC5jc3YiKSkKZnN0X3JhbmRvbSA9IHJlYWRyOjpyZWFkX3RzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI5XzEwMGtfcm5kbV9mc3QudHh0IikpICU+JSAKICBkcGx5cjo6bXV0YXRlKGhpdF9jb250cm9sID0gImhpdCIpCgojIGJpbmQKZnN0X291dCA9IGRwbHlyOjpiaW5kX3Jvd3MoZnN0X291dCwgZnN0X3JhbmRvbSkKCiMgZmFjdG9yCmZzdF9vdXQkcGhlbm90eXBlIDwtIGZhY3Rvcihmc3Rfb3V0JHBoZW5vdHlwZSwgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmIpCmBgYAoKIyMjIFBsb3QgCgojIyMjICpGc3QqIGRlbnNpdHkKCiMjIyMjIEZhY2V0cwoKYGBge3J9CmZzdF9vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBnZ3Bsb3QoYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgICBmYWNldF93cmFwKH5waGVub3R5cGUpICsKICAgIHlsYWIoIkRlbnNpdHkiKSArCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wcmltYXJ5KSArCiAgICBndWlkZXMoZmlsbCA9IEYpCgpgYGAKCiMjIyMjIFJpZGdlcwoKYGBge3IsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9JzUwJScsIGZpZy5jYXAgPSAiSGl0cyB2cyBjb250cm9scyJ9CmZzdF9vdXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaGl0X2NvbnRyb2wgPT0gImhpdCIpICU+JSAKICBkcGx5cjo6bXV0YXRlKHBoZW5vdHlwZSA9IGZhY3RvcihwaGVub3R5cGUsIGxldmVscyA9IHJldih0cmFpdF9sZXZlbHNfdmVyYikpKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9kZW5zaXR5X3JpZGdlczIobWFwcGluZyA9IGFlcyh4ID0gRnN0LCB5ID0gcGhlbm90eXBlLCBmaWxsID0gcGhlbm90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIHlsYWIobGFiZWwgPSBOVUxMKSArCiAgICB0aGVtZV9idygpICsKICAgIGd1aWRlcyhmaWxsID0gRikgKwogICAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBleHBhbmRfc2NhbGUoYWRkID0gYygwLjIsIDIuMykpKQpgYGAKCiMgRmluYWwKCiMjIEdldCBtZWRpYW4gKkZzdCogYW5kIDAuOSBwZXJjZW50aWxlCgojIyMgUmVhZCBpbiBkYXRhCgpgYGB7cn0KIyBSZWFkIGluIGRhdGEKZnN0X291dCA9IHJlYWRyOjpyZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjEwMTI3X3Jlc3VsdHMvMjAyMTAxMjhfZnN0LmNzdiIpKQpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKQpmc3Rfb3V0JGhpdF9jb250cm9sID0gZmFjdG9yKGZzdF9vdXQkaGl0X2NvbnRyb2wsIGxldmVscyA9IGhpdF9jb250cm9sX2xldmVscykKCiMgU3BsaXQKZnN0X2xpc3QgPSBzcGxpdChmc3Rfb3V0LCBmID0gZnN0X291dCRoaXRfY29udHJvbCkKZnN0X2xpc3QgPSBsYXBwbHkoZnN0X2xpc3QsIGZ1bmN0aW9uKGRhdGFzZXQpewogIHNwbGl0KGRhdGFzZXQsIGYgPSBkYXRhc2V0JHBoZW5vdHlwZSkKfSkKYGBgCgojIyMgTWVkaWFuIGFuZCAwLjkgcGVyY2VudGlsZQoKRnJvbSB0aGUgYGhlbHBgIHBhZ2Ugb2YgYHdpbGNveG9uLnRlc3RgOgoKPiBOb3RlIHRoYXQgaW4gdGhlIHR3by1zYW1wbGUgY2FzZSB0aGUgZXN0aW1hdG9yIGZvciB0aGUgZGlmZmVyZW5jZSBpbiBsb2NhdGlvbiBwYXJhbWV0ZXJzIGRvZXMgbm90IGVzdGltYXRlIHRoZSBkaWZmZXJlbmNlIGluIG1lZGlhbnMgKGEgY29tbW9uIG1pc2NvbmNlcHRpb24pIGJ1dCByYXRoZXIgdGhlIG1lZGlhbiBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGEgc2FtcGxlIGZyb20geCBhbmQgYSBzYW1wbGUgZnJvbSB5LgoKYGBge3J9Cm13X291dCA9IHNwbGl0KGZzdF9vdXQsIGYgPSBmc3Rfb3V0JGhpdF9jb250cm9sKQptd19vdXQgPSBsYXBwbHkobXdfb3V0LCBmdW5jdGlvbihkYXRhc2V0KXsKICBzcGxpdF9kYXRhID0gc3BsaXQoZGF0YXNldCwgZiA9IGRhdGFzZXQkcGhlbm90eXBlKQogIAogIG91dCA9IGxhcHBseShzcGxpdF9kYXRhLCBmdW5jdGlvbihwaGVubyl7CiAgICBzdGF0cyA9IGxpc3QoKQogICAgCiAgICAjIEdldCBtZWRpYW4gYW5kIDkwIHBlcmNlbnRpbGUKICAgIHN0YXRzW1sic3RhdHMiXV0gPSBjKCJtZWRpYW4iID0gbWVkaWFuKHBoZW5vJEZzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwaGVubyRGc3QsIHByb2JzID0gMC45KSkKICAgIAogICAgIyBHZXQgTWFubi1XaGl0bmV5IHJlc3VsdHMKICAgIHN0YXRzW1sibXdfb3V0Il1dID0gd2lsY294LnRlc3QoeCA9IHNwbGl0X2RhdGFbWyJIZWlnaHQiXV0kRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcGhlbm8kRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJsZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFpcmVkID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZi5pbnQgPSBUKQogICAgCiAgICAjIEdldCBLUyByZXN1bHRzCiAgICBzdGF0c1tbImtzX291dCJdXSA9IGtzLnRlc3QoeCA9IHNwbGl0X2RhdGFbWyJIZWlnaHQiXV0kRnN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwaGVubyRGc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCgogICAgcmV0dXJuKHN0YXRzKQogIH0pIyAlPiUgCiAgICAjZHBseXI6OmJpbmRfcm93cyguaWQgPSAicGhlbm90eXBlIikKICAKICAKICByZXR1cm4ob3V0KQoKfSkKCndpbGNveC50ZXN0KGZzdF9saXN0JGhpdCRIZWlnaHQkRnN0LCBmc3RfbGlzdCRoaXQkSGVpZ2h0JEZzdCwKICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAibGVzcyIpCgprcy50ZXN0KGZzdF9saXN0JGhpdCRIZWlnaHQkRnN0LCBmc3RfbGlzdCRoaXQkYEVkdWNhdGlvbmFsIGF0dGFpbm1lbnRgJEZzdCkKCiMgQ29sbGFwc2UgaW50byBERgptd19nZyA9IGxhcHBseShtd19vdXQsIGZ1bmN0aW9uKGRhdGFzZXQpewogIG91dCA9IGxhcHBseShkYXRhc2V0LCBmdW5jdGlvbihwaGVubyl7CiAgICAjIFB1dCBrZXkgZGF0YSBpbnRvIGRhdGEgZnJhbWUKICAgIGRmID0gYygiTUVESUFOIiA9IHBoZW5vW1sic3RhdHMiXV1bWyJtZWRpYW4iXV0sCiAgICAgICAgICAgIjkwJSIgPSBwaGVub1tbInN0YXRzIl1dW1siOTAlIl1dLAogICAgICAgICAgIHBoZW5vW1sibXdfb3V0Il1dJHN0YXRpc3RpYywKICAgICAgICAgICAiUF9NVyIgPSBwaGVub1tbIm13X291dCJdXSRwLnZhbHVlLAogICAgICAgICAgICJDT05GX0xPV0VSIiA9IHBoZW5vW1sibXdfb3V0Il1dJGNvbmYuaW50WzFdLAogICAgICAgICAgICJDT05GX1VQUEVSIiA9IHBoZW5vW1sibXdfb3V0Il1dJGNvbmYuaW50WzJdLAogICAgICAgICAgIHBoZW5vW1sia3Nfb3V0Il1dJHN0YXRpc3RpYywKICAgICAgICAgICAiUF9LUyIgPSBwaGVub1tbImtzX291dCJdXSRwLnZhbHVlKQoKICAgIHJldHVybihkZikKICB9KSAlPiUgCiAgICBkcGx5cjo6YmluZF9yb3dzKC5pZCA9ICJQSEVOTyIpCiAgCiAgcmV0dXJuKG91dCkKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoLmlkID0gIkhJVF9DT05UUk9MIikKCiMgRmFjdG9yCm13X2dnJFBIRU5PIDwtIGZhY3Rvcihtd19nZyRQSEVOTywgbGV2ZWxzID0gdHJhaXRfbGV2ZWxzX3ZlcmIpCm13X2dnJEhJVF9DT05UUk9MID0gZmFjdG9yKG13X2dnJEhJVF9DT05UUk9MLCBsZXZlbHMgPSBoaXRfY29udHJvbF9sZXZlbHMpCmBgYAoKUGxvdAoKYGBge3J9Cm13X2dnICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2NvbChhZXMoUEhFTk8sIC1sb2cxMChQX01XKSwgZmlsbCA9IFBIRU5PKSkgKwogICAgZmFjZXRfd3JhcCh+SElUX0NPTlRST0wpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKCJNYW5uLVdoaXRuZXkgcC12YWx1ZXMiKQoKYGBgCmBgYHtyfQptd19nZyAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9jb2woYWVzKFBIRU5PLCAtbG9nMTAoUF9LUyksIGZpbGwgPSBQSEVOTykpICsKICAgIGZhY2V0X3dyYXAofkhJVF9DT05UUk9MKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3ByaW1hcnkpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ2d0aXRsZSgiS1MtdGVzdCBwLXZhbHVlcyIpCgpgYGAKKjIwMjEwMjAxKgoKIyBGaW5hbCBwbG90cwoKIyMgKkZzdCogZGVuc2l0eSBmb3IgaGl0cwoKIyMjIFJlYWQgaW4gZGF0YQoKYGBge3J9CiMgcmVhZApmc3Rfb3V0ID0gcmVhZHI6OnJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMTAxMjdfcmVzdWx0cy8yMDIxMDEyOF9mc3QuY3N2IikpCgojIGZhY3Rvcgpmc3Rfb3V0JHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dCRwaGVub3R5cGUsIGxldmVscyA9IHRyYWl0X2xldmVsc192ZXJiKQpgYGAKCmBgYHtyfQpmc3Rfb3V0ICU+JSAKICBkcGx5cjo6ZmlsdGVyKGhpdF9jb250cm9sID09ICJoaXQiKSAlPiUgCiAgZHBseXI6Om11dGF0ZShwaGVub3R5cGUgPSBmYWN0b3IocGhlbm90eXBlLCBsZXZlbHMgPSByZXYodHJhaXRfbGV2ZWxzX3ZlcmIpKSkgJT4lCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IEZzdCwgeSA9IHBoZW5vdHlwZSwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICAgIHN0YXRfZGVuc2l0eV9yaWRnZXMoYWVzKHNjYWxlID0gMiksCiAgICAgICAgICAgICAgICAgICAgICAgIGdlb20gPSAiZGVuc2l0eV9yaWRnZXNfZ3JhZGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICBjYWxjX2VjZGYgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZXMgPSBjKDAuNSwgMC45KSwKICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGVfbGluZXMgPSBUKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxfcHJpbWFyeSkgKwogICAgeWxhYihsYWJlbCA9IE5VTEwpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ3VpZGVzKGZpbGwgPSBGKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbihhZGQgPSBjKDAuMiwgMi4zKSkpCgpgYGAKCg==